Read the code for this tutorial here
Overview
This tutorial walks you through creating an application that sends a SNIP-20 tokens from the selected wallet to another one. You will learn how to use the SNIP-20 built-in definition and how to set up a simple architecture for your contracts in general.
Requirements
In order to go through this tutorial you'll need to have a React created. You can find how to do it here . Also, install your dependencies and install Griptape:
Copy # With npm
npm install && npm install @stakeordie/griptape.js
# With yarn
yarn && yarn add @stakeordie/griptape.js
Getting Started
This tutorial consist of these steps:
Bootstrap the application
Create a SNIP-20 client contract
Create a viewing key for the SNIP-20 token
Display current token balance
Create form to execute a message that sends tokens
Grip your application
Go to the src/index.js
and import gripApp
and getKeplrAccountProvider
from @stakeordie/griptape.js
package.
Copy import React from "react" ;
import ReactDOM from "react-dom" ;
import App from "./App" ;
import {
gripApp ,
getKeplrAccountProvider
} from "@stakeordie/griptape.js" ;
const restUrl = "https://api.pulsar.griptapejs.com" ;
const provider = getKeplrAccountProvider ();
function runApp () {
ReactDOM .render (
< React.StrictMode >
< App />
</ React.StrictMode > ,
document .getElementById ( "root" )
);
}
gripApp (restUrl , provider , runApp);
Bootstrap the application
Open up src/App.js
and add a button to bootstrap the application.
Copy import React , { useState , useEffect } from "react" ;
import { bootstrap , onAccountAvailable } from "@stakeordie/griptape.js" ;
function App () {
const [ isConnected , setIsConnected ] = useState ( false );
useEffect (() => {
const removeOnAccountAvailable = onAccountAvailable (() => {
setIsConnected ( true );
});
return () => {
removeOnAccountAvailable
}
} , []);
return (
<>
< h1 >Hello, Transactions!</ h1 >
< p >Is connected? {isConnected ? "Yes" : "No" }</ p >
< button onClick = {() => bootstrap ()} disabled = {isConnected}>
Bootstrap
</ button >
</>
);
}
export default App;
Create a SNIP-20 client contract
Create a new directory in the src
directory called contracts
and add a new file called sscrt.js
. This will the contract we will be interacting with.
Copy import {
createContractClient ,
snip20Def
} from "@stakeordie/griptape.js" ;
export const sscrt = createContractClient ({
id : "sscrt" ,
at : "secret18vd8fpwxzck93qlwghaj6arh4p7c5n8978vsyg" ,
definition : snip20Def
});
Create a viewing key for the SNIP-20 token
Create a button that create and sets a new viewing key for the SNIP-20 token. Set the viewing key using the viewingKeyManager
object by importing it.
Copy import React , { useState , useEffect } from "react" ;
import {
bootstrap ,
onAccountAvailable ,
onViewingKeyCreated ,
viewingKeyManager
} from "@stakeordie/griptape.js" ;
import { sscrt } from "./contracts/sscrt" ;
function App () {
const [ isConnected , setIsConnected ] = useState ( false );
const [ isMessageLoading , setMessageLoading ] =
useState ( false );
useEffect (() => {
const removeOnAccountAvailable = onAccountAvailable (() => {
setIsConnected ( true );
hasViewingKey ();
});
const removeOnViewingKeyCreated = onViewingKeyCreated (() => {
hasViewingKey ();
});
return () => {
removeOnAccountAvailable ();
removeOnViewingKeyCreated ();
}
} , []);
async function createViewingKey () {
setMessageLoading ( true );
try {
const result = await sscrt .createViewingKey ();
if ( result .isEmpty ()) return ;
const { create_viewing_key: { key } } =
result .parse ();
viewingKeyManager .add (sscrt , key);
} finally {
setMessageLoading ( false );
}
}
function hasViewingKey () {
const key = viewingKeyManager .get ( sscrt .at);
return typeof key !== "undefined" ;
}
return (
<>
< h1 >Hello, Transactions!</ h1 >
< p >Is connected? { isConnected ? "Yes" : "No" }</ p >
< p >
Has viewing key? { hasViewingKey () ? "Yes" : "No" }
</ p >
< button
onClick = {() => bootstrap ()}
disabled = {isConnected}
>
Connect
</ button >
< button
onClick = {() => createViewingKey ()}
disabled = {
isMessageLoading
|| hasViewingKey ()
|| ! isConnected
}
>
Create Viewing Key
</ button >
</>
);
}
export default App;
Display current account token balance
Create a button and bind a click event to a function that queries the token balance of the sscrt
client contract.
Copy import React , { useState , useEffect } from "react" ;
import {
bootstrap ,
onAccountAvailable ,
onViewingKeyCreated ,
viewingKeyManager ,
coinConvert
} from "@stakeordie/griptape.js" ;
import { sscrt } from "./contracts/sscrt" ;
function App () {
const [ isConnected , setIsConnected ] = useState ( false );
const [ isMessageLoading , setMessageLoading ] =
useState ( false );
const [ isQueryLoading , setQueryLoading ] =
useState ( false );
const [ balance , setBalance ] = useState ( "" );
useEffect (() => {
const removeOnAccountAvailable = onAccountAvailable (() => {
setIsConnected ( true );
hasViewingKey ();
});
const removeOnViewingKeyCreated = onViewingKeyCreated (() => {
hasViewingKey ();
});
return () => {
removeOnAccountAvailable ();
removeOnViewingKeyCreated ();
}
} , []);
async function createViewingKey () {
setMessageLoading ( true );
try {
const result = await sscrt .createViewingKey ();
if ( result .isEmpty ()) return ;
const { create_viewing_key: { key } } =
result .parse ();
viewingKeyManager .add (sscrt , key);
} finally {
setMessageLoading ( false );
}
}
function hasViewingKey () {
const key = viewingKeyManager .get ( sscrt .at);
return typeof key !== "undefined" ;
}
async function getBalance () {
if ( ! hasViewingKey ()) return ;
setQueryLoading ( true );
try {
const { balance: { amount: result } } =
await sscrt .getBalance ();
const amount = coinConvert (result , 6 , "human" );
setBalance (amount);
} finally {
setQueryLoading ( false );
}
}
return (
<>
< h1 >Hello, Transactions!</ h1 >
< p >Is connected? { isConnected ? "Yes" : "No" }</ p >
< p >
Has viewing key? { hasViewingKey () ? "Yes" : "No" }
</ p >
< p >SNIP-20 Token Balance: { balance }</ p >
< button
onClick = {() => bootstrap ()}
disabled = {isConnected}
>
Connect
</ button >
< button
onClick = {() => createViewingKey ()}
disabled = {
isMessageLoading
|| hasViewingKey ()
|| ! isConnected
}
>
Create Viewing Key
</ button >
< button
onClick = {() => getBalance ()}
disabled = {isQueryLoading}
>
Get Balance
</ button >
</>
);
}
export default App;
Create form to execute a message that sends tokens
Create a form to input an address and an amount and execute the send
message on the sscrt
contract.
Copy import React , { useState , useEffect } from "react" ;
import {
bootstrap ,
onAccountAvailable ,
onViewingKeyCreated ,
viewingKeyManager ,
coinConvert
} from "@stakeordie/griptape.js" ;
import { sscrt } from "./contracts/sscrt" ;
function App () {
const [ isConnected , setIsConnected ] = useState ( false );
const [ isMessageLoading , setMessageLoading ] =
useState ( false );
const [ isQueryLoading , setQueryLoading ] =
useState ( false );
const [ balance , setBalance ] = useState ( "" );
const [ address , setAddress ] = useState ( "" );
const [ amount , setAmount ] = useState ( 0 );
useEffect (() => {
const removeOnAccountAvailable = onAccountAvailable (() => {
setIsConnected ( true );
hasViewingKey ();
});
const removeOnViewingKeyCreated = onViewingKeyCreated (() => {
hasViewingKey ();
});
return () => {
removeOnAccountAvailable ();
removeOnViewingKeyCreated ();
}
} , []);
async function createViewingKey () {
setMessageLoading ( true );
try {
const result = await sscrt .createViewingKey ();
if ( result .isEmpty ()) return ;
const { create_viewing_key: { key } } =
result .parse ();
viewingKeyManager .add (sscrt , key);
} finally {
setMessageLoading ( false );
}
}
function hasViewingKey () {
const key = viewingKeyManager .get ( sscrt .at);
return typeof key !== "undefined" ;
}
async function getBalance () {
if ( ! hasViewingKey ()) return ;
setQueryLoading ( true );
try {
const { balance: { amount: result } } =
await sscrt .getBalance ();
const amount = coinConvert (result , 6 , "human" );
setBalance (amount);
} finally {
setQueryLoading ( false );
}
}
async function sendTokens (e) {
e .preventDefault ();
if ( ! address || ! amount) return ;
setMessageLoading ( true );
try {
const theAmount = coinConvert (amount , 6 , "machine" );
await sscrt .send (address , theAmount);
setAddress ( "" );
setAmount ( 0 );
} finally {
setMessageLoading ( false );
}
}
return (
<>
< h1 >Hello, Transactions!</ h1 >
< p >Is connected? { isConnected ? "Yes" : "No" }</ p >
< p >
Has viewing key? { hasViewingKey () ? "Yes" : "No" }
</ p >
< p >SNIP-20 Token Balance: { balance }</ p >
< button
onClick = {() => bootstrap ()}
disabled = {isConnected}
>
Connect
</ button >
< button
onClick = {() => createViewingKey ()}
disabled = {
isMessageLoading
|| hasViewingKey ()
|| ! isConnected
}
>
Create Viewing Key
</ button >
< button
onClick = {() => getBalance ()}
disabled = {isQueryLoading}
>
Get Balance
</ button >
< form onSubmit = {sendTokens}>
< input
type = "text"
placeholder = "Address to send to"
onChange = {(e) => setAddress ( e . target .value)}
value = {address}
/>
< input
type = "number"
placeholder = "Amount to send"
onChange = {(e) => setAmount ( e . target .value)}
value = {amount}
/>
< button disabled = {isMessageLoading}>
Send tokens
</ button >
</ form >
</>
);
}
export default App;