# Kids Book NFT AI Agent w/ BNB Chain

In our previous case study, we demonstrated [how to create a children's book AI agent using ShellAgent with DeepSeek](https://docs.myshell.ai/create/shellagent-mode/example/child-book). 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.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FtdaLzAXPWGlKNfttn2Uq%2Fimage.png?alt=media&#x26;token=5eab4d3d-8de0-45e1-b89f-3742d614760b" alt="" width="375"><figcaption></figcaption></figure>

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

{% stepper %}
{% step %}
[**Create a new agent**](https://app.myshell.ai/robot-workshop/create) from MyShell Workshop and fill in the required fields.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FbLhgxSZdIVDpzdgjuVVJ%2Fimage.png?alt=media&#x26;token=63371f76-5e94-4a9c-9436-1799653527aa" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Choose ShellAgent Mode**

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

{% file src="<https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FzWJQfv4F6VQwmS5XtZep%2FKids%20Book%20NFT%20AI%20Agent.json?alt=media&token=3777ab26-9c81-469f-87df-8619c6401763>" %}

{% hint style="info" %}
Right-click on the above .json link and select "**Save link as...**" to download the .json file
{% endhint %}

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FJJwLJMjdlrQJr3e5cQIM%2Fimage.png?alt=media&#x26;token=c3c193ad-6654-4425-a048-d4c263d389ae" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**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](#notice) if you encounter errors.

{% endstep %}
{% endstepper %}

### Cloud selection

{% stepper %}
{% step %}
**Create an agent from templates**

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2F3CS3xUEB3I6mLAJElnz4%2Fimage.png?alt=media&#x26;token=6b478208-3239-444d-984e-d3456dc39b3b" alt="" width="375"><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Choose the template**

Choose the template of <mark style="color:purple;">Kids Book NFT AI Agent</mark>.
{% endstep %}

{% step %}
**Save your configuration**

Click the **Save** button on the top right corner of the interface

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FHk5MAgyURAjXEMdjtryh%2Fimage.png?alt=media&#x26;token=8172df78-c257-4a92-9a0a-87f8664ba02f" alt="" width="375"><figcaption></figcaption></figure>
{% endstep %}

{% step %}
**Go to the** [**Deployment chapter**](#deployment)

{% endstep %}
{% endstepper %}

## Build the agent from 0 to 1

{% hint style="info" %}
First, please visit [ShellAgent](https://shellagent.myshell.ai/).
{% endhint %}

### Create a blank agent canvas

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FW8nNANOZuf6b4yWJ8ye5%2Fimage.png?alt=media&#x26;token=abf3a599-1bd6-4318-98d7-648351cd845a" alt="" width="241"><figcaption></figcaption></figure>

### Enter the name and choose the type

Choose the **MyShell Agent** type.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2Fpk5KkWqMcIjxIvl9djdW%2Fimage.png?alt=media&#x26;token=63c342ed-c0b2-4b7c-a436-4f40bcafac9c" alt="" width="375"><figcaption></figcaption></figure>

### Edit the Start state

Context variables are like a thread that connects everything within ShellAgent.

{% stepper %}
{% step %}
Add `story`, `reply`, and `calldata` as `text` Type variable.

{% endstep %}

{% step %}
Add `image` as `image` Type variable.

{% endstep %}

{% step %}
Add `USER_WALLET` by `Select System Context` variable and set it as `text` Type variable.

{% endstep %}

{% step %}
Add `nft_address` as `text` Type variable and enter `0x0Cb5c79D59847ACEdCE11651d18bB968Cf236aC4`

{% endstep %}
{% endstepper %}

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FpXKAIVroUBKwraDvyJo2%2Fimage.png?alt=media&#x26;token=c6456809-9d83-420e-8933-c25c0a96cf72" alt="" width="375"><figcaption></figcaption></figure>

### **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!*

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FI91TNlK7lBN9lU4M5SM5%2Fimage.png?alt=media&#x26;token=ba97269e-3295-411b-9d13-a361af24a9a0" alt="" width="365"><figcaption></figcaption></figure>

### Create a new state: Generate

In the **Input** section, add prompt as `text` **Type** variable and set **Source** as `IM`.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2FsxZf4ns8E1oD1yvdfLOY%2Fimage.png?alt=media&#x26;token=75ec74f9-0ed9-49a7-b72c-a81941511b65" alt="" width="375"><figcaption></figcaption></figure>

In the **Task** section, add `DeepSeek R1` widget.

{% stepper %}
{% step %}
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.
```

{% endstep %}

{% step %}
In Input - **user\_prompt**, select **Ref mode** and choose `Input` / `prompt`.

{% endstep %}

{% step %}
In Input - **hide\_think**, check it.

{% endstep %}
{% endstepper %}

In the **Task** section, add `Flux.1-dev` widget.

{% stepper %}
{% step %}
In Input - **prompt**, select **Code mode** and add the prompts below.

```
Picture book style. {{Task.DeepSeek R1#1.reply}}
```

{% endstep %}
{% endstepper %}

In the **Task** section, add `Contract Call Encoder` widget.

{% stepper %}
{% step %}
In Input - **abi**, select **Code mode** and add the code below.

{% code overflow="wrap" lineNumbers="true" %}

```json
[
  {
    "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"
  }
]
```

{% endcode %}
{% endstep %}

{% step %}
In Input - **function\_name**, enter `safeMint`.

{% endstep %}

{% step %}
In Input - **args**, select **Code mode** and add the code below.

{% code lineNumbers="true" %}

```json
[
  "{{Context.USER_WALLET}}",
  "ipfs://{{Task.Metadata Uploader#1.result.IpfsHash}}"
]
```

{% endcode %}
{% endstep %}
{% endstepper %}

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/reply
* Variable 2: `Context/image` -> Task/Flux.1-dev#1/url
* Variable 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 **Input**s.

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` to `Intro` , that’s default.
* `Intro` uses the type box connects to `Generate` , this means that `Generate` gets input from user’s input at `Intro`'s type box.
* `Generate` connects to `Display` via the `“+”` sign, means no additional user action is needed
* At `Display` state
  * `Auto-write` button connects to `Auto-write` state
  * `Restart` button connects to `Restart` state
  * `Mint` button connects to `Display` state
  * Type box (means users wants to keep writing the story) goes to `Generate`
* `Auto-write` connects to `Display` via the `“+”` sign
* `Restart` 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](https://app.myshell.ai/robot-workshop/create) from MyShell Workshop for deployment and fill in the required fields.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2F94HdYkgjE2BeDVlRuH0C%2Fimage.png?alt=media&#x26;token=62bbe5ff-3eed-4fd6-9efe-dc4094f04393" alt="" width="563"><figcaption></figcaption></figure>

### Choose ShellAgent Mode

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

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2F5YSES3quqqBHTpecQ2LO%2Fimage.png?alt=media&#x26;token=c00a145a-766c-4faa-b792-47f15c44fc54" alt=""><figcaption></figcaption></figure>

### 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.

<figure><img src="https://2975681513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDtmZwLatyyBFLUK6P6Pj%2Fuploads%2Ff7pjVXUrsIx48MMLbQxO%2Fimage.png?alt=media&#x26;token=5c404d10-8c17-46fb-b0b4-6433cfb30c22" alt="" width="321"><figcaption></figcaption></figure>
