Kids Book NFT AI Agent w/ BNB Chain
Create a Kids Book AI agent integrated with NFT minting function on BNB Chain
In our previous case study, we demonstrated how to create a children's book AI agent using ShellAgent with DeepSeek. Following a significant update to ShellAgent, AI agents on MyShell now support on-chain interactions including blockchain data analysis, transaction execution, and smart contract engagement.
This case study explores how we integrated NFT minting capabilities into our kids book AI agent. After the agent generates a story, users can opt to mint an NFT on BNB Chain. BNB Chain offers substantially lower transaction fees than Ethereum, making it ideal for high-volume operations like NFT minting. This integration creates a natural synergy of AI agents between multi-modal storytelling and BNB Chain's strengths.
Clarify your idea first
Before getting started, you need a clear vision of how the AI agent will interact with users to accomplish its tasks, setting realistic expectations for different models handling various functions.
Below is a simplified overview of how the Kids Book NFT AI Agent works. We use DeepSeek to transform user text input into prompts that image-generation models can understand, then display both the story and images. Users can continue writing the story, start a new one, or ask DeepSeek to continue the narrative. After the story generation, users can choose whether to mint a NFT for the story on BNB Chain.

Main functions and related widgets:
Story writer: DeepSeek
Image generator: Flux
NFT image uploader (via IPFS): Metadata Uploader
NFT minting (on BNB Chain): Contract Call Encoder
A quickstart template for beginners (optional)
Please skip this chapter and go to the next chapter if you are a veteran or you want to learn more about the processes.
There are two ways for quickstart: local upload or cloud selection.
Local upload
Create a new agent from MyShell Workshop and fill in the required fields.

Choose ShellAgent Mode
Choose ShellAgent Mode - Local Upload and then upload the .json configuration below:

Save and publish
Save it and you may publish it if you have a creator pass.
You are all set. Enjoy your agent!
P.S. You may also read this notice if you encounter errors.
Cloud selection
Create an agent from templates

Choose the template
Choose the template of Kids Book NFT AI Agent.
Save your configuration
Click the Save button on the top right corner of the interface

Go to the Deployment chapter
Build the agent from 0 to 1
Create a blank agent canvas

Enter the name and choose the type
Choose the MyShell Agent type.

Edit the Start state
Context variables are like a thread that connects everything within ShellAgent.
Add story
, reply
, and calldata
as text
Type variable.
Add image
as image
Type variable.
Add USER_WALLET
by Select System Context
variable and set it as text
Type variable.
Add nft_address
as text
Type variable and enter 0x0Cb5c79D59847ACEdCE11651d18bB968Cf236aC4

Edit the Intro state
Enter some intro messages in the Text field, like Tell me a story. I will draw a picture book for you!

Create a new state: Generate
In the Input section, add prompt as text
Type variable and set Source as IM
.

In the Task section, add DeepSeek R1
widget.
In Input - system_prompt, select Code mode and add the prompts below.
# Role
You are a Picture Book Page Generator who assists users in creating imaginative sentences for each page of a picture book based on user prompts. Each sentence should be concise and vivid enough to be illustrated in a single picture, making it visually appealing and child-friendly.
## Skills
### Skill 1: Enhance Sentence Descriptions
- When a user provides a vague sentence, ensure it is vivid and suitable for illustration if necessary, without adding additional details.
- Maintain the child-friendly tone and ensure the sentence is easy to visualize.
### Skill 2: Create Visualizable Sentences
- Craft sentences that are concise and rich in imagery, enabling them to be effectively illustrated in one picture.
- Focus on key elements that are engaging and spark imagination in children.
## Output Format
Don't enclose output in single (') or double quotes("), provide it as it is.
## Constraints:
- Only enhance or create sentences based on user prompts.
- Ensure the output is suitable for children and focuses on positive, imaginative themes.
- Keep each sentence concise and limited to one sentence for a single-picture visualization.
- One sentence should have fewer than 30 words.
- Do not add any details beyond what the user provides.
In Input - user_prompt, select Ref mode and choose Input
/ prompt
.
In Input - hide_think, check it.
In the Task section, add Flux.1-dev
widget.
In Input - prompt, select Code mode and add the prompts below.
Picture book style. {{Task.DeepSeek R1#1.reply}}
In the Task section, add Contract Call Encoder
widget.
In Input - abi, select Code mode and add the code below.
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ERC721IncorrectOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ERC721InsufficientApproval",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC721InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
}
],
"name": "ERC721InvalidOperator",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "ERC721InvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC721InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC721InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ERC721NonexistentToken",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "approved",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "ApprovalForAll",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "_fromTokenId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_toTokenId",
"type": "uint256"
}
],
"name": "BatchMetadataUpdate",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "_tokenId",
"type": "uint256"
}
],
"name": "MetadataUpdate",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "getApproved",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "operator",
"type": "address"
}
],
"name": "isApprovedForAll",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ownerOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "string",
"name": "uri",
"type": "string"
}
],
"name": "safeMint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "safeTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "safeTransferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"internalType": "bool",
"name": "approved",
"type": "bool"
}
],
"name": "setApprovalForAll",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "tokenURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
In Input - function_name, enter safeMint
.
In Input - args, select Code mode and add the code below.
[
"{{Context.USER_WALLET}}",
"ipfs://{{Task.Metadata Uploader#1.result.IpfsHash}}"
]
In the Output section, add 3 new variables, edit the Variable names by Select Output, and choose the variables accordingly
Variable 1:
Context/reply
-> Task/DeepSeek R1#1/replyVariable 2:
Context/image
-> Task/Flux.1-dev#1/urlVariable 3:
Context/calldata
-> Task/Contract Call Encoder#1/encoded_data
Create a new state: Display
In the Output section, add a new variable, edit the Variable name as Context/story
by Select Output, select Code mode and add the code below.
{{Context.story}}
{{Context.reply}}
In the Message section, set Text and Image variables as:
Text -> (Ref mode) Context/reply
Image -> (Ref mode) Context/image
Then add 3 buttons:
Button#1: Auto write
Button#2: Restart
Button#3: Mint
Duplicate the Generate state and name it: Auto-write
After duplication, delete all the Inputs.
In the Task section, edit the DeepSeek R1
widget by replacing the prompts with:
# Role
You are a Picture Story Completer who assists users in creating imaginative sentences for each page of a picture book based on user-provided unfinished stories. Each sentence should be concise and vivid enough to be illustrated in a single picture, making it visually appealing and child-friendly.
## Skills
### Skill 1: Understand Story and Continue
- When a user provides an unfinished story, understand the story's plots, characters, and themes.
- Continue with a sentence that is vivid and suitable for illustration, aligning with the story's plots, characters, and themes.
- Maintain a child-friendly tone and ensure the sentence is easy to visualize.
### Skill 2: Create Visualizable Sentences
- Craft sentences that are concise and rich in imagery, enabling them to be effectively illustrated in one picture.
- Focus on key elements that engage and spark imagination in children.
## Output Format
Provide the output as it is, without enclosing it in single (') or double quotes (").
## Constraints:
- Only enhance or create sentences based on user prompts.
- Ensure the output is suitable for children and focuses on positive, imaginative themes.
- Keep each sentence concise and limited to one sentence for a single-picture visualization.
- Each sentence should have fewer than 30 words.
- Do not add any details beyond what the user provides.
In Input - user_prompt (inside the DeepSeek widget settings), select Ref mode and choose Context
/ Story
.
Create a new state: Restart
In the Output section, add a new variable, edit the Variable names as Context/story
by Select Output.
Connect all the states
We’ve completed all necessary states at ShellAgent, now you need to add transitions to make the state machine running. The “+”
sign on the left and right side of each state node represents the flow of states. Under the Transition section of each state represents where user interaction will change the flow of states.
Le’s go there one by one:
Start
toIntro
, that’s default.Intro
uses the type box connects toGenerate
, this means thatGenerate
gets input from user’s input atIntro
's type box.Generate
connects toDisplay
via the“+”
sign, means no additional user action is neededAt
Display
stateAuto-write
button connects toAuto-write
stateRestart
button connects toRestart
stateMint
button connects toDisplay
stateType box (means users wants to keep writing the story) goes to
Generate
Auto-write
connects toDisplay
via the“+”
signRestart
connects to Intro via the“+”
sign
Save
Make sure to click the save button in ShellAgent (even we have the auto-save function).
That’s it! You’ve completed the creation of your agent in ShellAgent, congrats!
We will introduce how to deploy the MyShell Agent you've created on MyShell in the next chapter.
Deployment
Create a new agent
Create a new agent from MyShell Workshop for deployment and fill in the required fields.

Choose ShellAgent Mode
Choose ShellAgent Mode and select the configuration you created in ShellAgent.

Save and publish
Save it and you may publish it if you have a creator pass.
Enjoy your AI agent on MyShell.
Notice
You may need to Clear Memory first to avoid errors while using.

Last updated