This example application implements generative non-fungible tokens (NFTs). It is based on the Non-Fungible Token Example Application, with the difference being that instead of representing the token payload as an arbitrary sequence of bytes, it is represented as a string generated by a large-language model.
The main purpose of this application is to show how to run an artificial intelligence model in the application service, allowing it to generate strings used as the contents of non-fungible tokens. The application service runs on the client (usually inside the wallet), which means it executes off-chain. In this example, it is used to prepare the mint operation by running the AI model to generate the token contents from a user provided prompt. The resulting operation can then be placed by the wallet in a block proposal, minting the token on-chain.
This application's contract is mostly identical to the one from the NFT Example application. The only difference is the change of the token payload to be a text string instead of an arbitrary sequence of bytes. Like the NFT example application, this also shows cross-chain messages, demonstrating how NFTs can be minted, transferred, and claimed across different chains, emphasizing the instantiation and auto-deployment of application instances within the Linera blockchain ecosystem.
For more details on the application's contract and how it manages multiple different NFTs, see the NFT example.
Most of this can be referred to the fungible app README, except for at the end when compiling and publishing the bytecode, what you'll need to do will be slightly different.
export PATH="$PWD/target/debug:$PATH"
source /dev/stdin <<<"$(linera net helper 2>/dev/null)"
linera_spawn_and_read_wallet_variables linera net up --testing-prng-seed 37
Compile the non-fungible
application WebAssembly binaries, and publish them as an application bytecode:
(cd examples/gen-nft && cargo build --release --target wasm32-unknown-unknown)
BYTECODE_ID=$(linera publish-bytecode \
examples/target/wasm32-unknown-unknown/release/gen_nft_{contract,service}.wasm)
Here, we stored the new bytecode ID in a variable BYTECODE_ID
to be reused later.
Unlike fungible tokens, each NFT is unique and identified by a unique token ID. Also unlike fungible tokens, when creating the NFT application you don't need to specify an initial state or parameters. NFTs will be minted later.
Refer to the fungible app README to figure out how to list the chains created for the test in the default wallet, as well as defining some variables corresponding to these values.
linera wallet show
CHAIN_1=e476187f6ddfeb9d588c7b45d3df334d5501d6499b3f9ad5595cae86cce16a65 # default chain for the wallet
OWNER_1=7136460f0c87ae46f966f898d494c4b40c4ae8c527f4d1c0b1fa0f7cff91d20f # owner of chain 1
CHAIN_2=256e1dbc00482ddd619c293cc0df94d366afe7980022bb22d99e33036fd465dd # another chain in the wallet
OWNER_2=598d18f67709fe76ed6a36b75a7c9889012d30b896800dfd027ee10e1afd49a3 # owner of chain 2
To create the NFT application, run the command below:
APP_ID=$(linera create-application $BYTECODE_ID)
This will store the application ID in a new variable APP_ID
.
Operations such as minting NFTs, transferring NFTs, and claiming NFTs from other chains follow a similar approach to fungible tokens, with adjustments for the unique nature of NFTs.
First, a node service for the current wallet has to be started:
PORT=8080
linera service --port $PORT &
Type each of these in the GraphiQL interface and substitute the env variables with their actual values that we've defined above.
- Navigate to the URL you get by running
echo "http://localhost:8080/chains/$CHAIN_1/applications/$APP_ID"
. - To mint an NFT, run the query:
mutation {
mint(
minter: "User:$OWNER_1",
prompt: "Hello!"
)
}
- To check that it's assigned to the owner, run the query:
query {
ownedNfts(owner: "User:$OWNER_1")
}
Set the QUERY_RESULT
variable to have the result returned by the previous query, and TOKEN_ID
will be properly set for you.
Alternatively you can set the TOKEN_ID
variable to the tokenId
value returned by the previous query yourself.
TOKEN_ID=$(echo "$QUERY_RESULT" | jq -r '.ownedNfts[].tokenId')
- To check that it's there, run the query:
query {
nft(tokenId: "$TOKEN_ID") {
tokenId,
owner,
prompt,
minter,
}
}
- To check everything that it's there, run the query:
query {
nfts
}
- To transfer the NFT to user
$OWNER_2
, still on chain$CHAIN_1
, run the query:
mutation {
transfer(
sourceOwner: "User:$OWNER_1",
tokenId: "$TOKEN_ID",
targetAccount: {
chainId: "$CHAIN_1",
owner: "User:$OWNER_2"
}
)
}
Installing and starting the web server:
cd examples/gen-nft/web-frontend
npm install --no-save
# Start the server but not open the web page right away.
BROWSER=none npm start &
echo "http://localhost:3000/$CHAIN_1?app=$APP_ID&owner=$OWNER_1&port=$PORT"
echo "http://localhost:3000/$CHAIN_1?app=$APP_ID&owner=$OWNER_2&port=$PORT"
For the final part, refer to Fungible Token Example Application - Using web frontend.