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 Vue 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/main.js
and import gripApp and getKeplrAccountProvider from @stakeordie/griptape.js
package.
Copy import { createApp } from 'vue'
import App from './App.vue'
import {
gripApp,
getKeplrAccountProvider
} from '@stakeordie/griptape.js';
const restUrl = 'https://api.pulsar.griptapejs.com';
const provider = getKeplrAccountProvider();
function runApp() {
createApp(App)
.mount('#app')
}
gripApp(restUrl, provider, runApp);
Bootstrap the application
Open up src/App.js
and add a button to bootstrap the application.
Copy <template>
<div>
<h1>Hello, Transactions!</h1>
<button @click="connect" :disabled="isConnected">Connect</button>
</div>
</template>
<script>
import {
bootstrap,
onAccountAvailable,
onViewingKeyCreated,
viewingKeyManager,
coinConvert,
} from "@stakeordie/griptape.js";
export default {
data: () => ({
isConnected: false,
removeOnAccountAvailable: null,
removeOnViewingKeyCreated: null,
}),
mounted() {
this.removeOnAccountAvailable = onAccountAvailable(() => {
this.isConnected = true;
});
},
unmounted() {
this.removeOnAccountAvailable();
},
methods: {
async connect() {
await bootstrap();
},
}
</script>
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 <template>
<div>
<h1>Hello, Transactions!</h1>
<p>Is connected? {{ isConnected ? "Yes" : "No" }}</p>
<p>Has viewing key? {{ hasViewingKey() ? "Yes" : "No" }}</p>
<button @click="connect" :disabled="isConnected">Connect</button>
<button
@click="createViewingKey"
:disabled="isMessageLoading || hasViewingKey() || !isConnected"
>
Create Viewing Key
</button>
</div>
</template>
<script>
import {
bootstrap,
onAccountAvailable,
onViewingKeyCreated,
viewingKeyManager,
coinConvert,
} from "@stakeordie/griptape.js";
import { sscrt } from "../contracts/sscrt";
export default {
data: () => ({
isConnected: false,
isMessageLoading: false,
isQueryLoading: false,
balance: "",
address: "",
amount: "",
removeOnAccountAvailable: null,
removeOnViewingKeyCreated: null,
}),
mounted() {
this.removeOnAccountAvailable = onAccountAvailable(() => {
this.isConnected = true;
this.hasViewingKey();
});
this.removeOnViewingKeyCreated = onViewingKeyCreated(() => {
this.hasViewingKey();
});
},
unmounted() {
this.removeOnAccountAvailable();
this.removeOnViewingKeyCreated();
},
methods: {
async connect() {
await bootstrap();
},
hasViewingKey() {
const key = viewingKeyManager.get(sscrt.at);
return typeof key !== "undefined";
},
async createViewingKey() {
this.isMessageLoading = true;
try {
const result = await sscrt.createViewingKey();
if (result.isEmpty()) return;
const {
create_viewing_key: { key },
} = result.parse();
viewingKeyManager.add(sscrt, key);
} finally {
this.isMessageLoading = false;
}
},
},
};
</script>
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 <template>
<div>
<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 @click="connect" :disabled="isConnected">Connect</button>
<button
@click="createViewingKey"
:disabled="isMessageLoading || hasViewingKey() || !isConnected"
>
Create Viewing Key
</button>
<button @click="getBalance" @disabled="isQueryLoading">Get Balance</button>
</div>
</template>
<script>
import {
bootstrap,
onAccountAvailable,
onViewingKeyCreated,
viewingKeyManager,
coinConvert,
} from "@stakeordie/griptape.js";
import { sscrt } from "../contracts/sscrt";
export default {
data: () => ({
isConnected: false,
isMessageLoading: false,
isQueryLoading: false,
balance: "",
address: "",
amount: "",
removeOnAccountAvailable: null,
removeOnViewingKeyCreated: null,
}),
mounted() {
this.removeOnAccountAvailable = onAccountAvailable(() => {
this.isConnected = true;
this.hasViewingKey();
});
this.removeOnViewingKeyCreated = onViewingKeyCreated(() => {
this.hasViewingKey();
});
},
unmounted() {
this.removeOnAccountAvailable();
this.removeOnViewingKeyCreated();
},
methods: {
async connect() {
await bootstrap();
},
hasViewingKey() {
const key = viewingKeyManager.get(sscrt.at);
return typeof key !== "undefined";
},
async createViewingKey() {
this.isMessageLoading = true;
try {
const result = await sscrt.createViewingKey();
if (result.isEmpty()) return;
const {
create_viewing_key: { key },
} = result.parse();
viewingKeyManager.add(sscrt, key);
} finally {
this.isMessageLoading = false;
}
},
async getBalance() {
if (!this.hasViewingKey()) return;
this.isQueryLoading = true;
try {
const {
balance: { amount: result },
} = await sscrt.getBalance();
const amount = coinConvert(result, 6, "human");
this.balance = amount;
} finally {
this.isQueryLoading = false;
}
}
},
};
</script>
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 <template>
<div>
<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 @click="connect" :disabled="isConnected">Connect</button>
<button
@click="createViewingKey"
:disabled="isMessageLoading || hasViewingKey() || !isConnected"
>
Create Viewing Key
</button>
<button @click="getBalance" @disabled="isQueryLoading">Get Balance</button>
<form @submit="sendTokens">
<input
type="text"
placeholder="Address to send to"
@change="(e) => (this.address = e.target.value)"
:value="address"
/>
<input
type="number"
placeholder="Amount to send"
@change="(e) => (this.amount = e.target.value)"
:value="amount"
/>
<button :disabled="isMessageLoading">Send tokens</button>
</form>
</div>
</template>
<script>
import {
bootstrap,
onAccountAvailable,
onViewingKeyCreated,
viewingKeyManager,
coinConvert,
} from "@stakeordie/griptape.js";
import { sscrt } from "../contracts/sscrt";
export default {
data: () => ({
isConnected: false,
isMessageLoading: false,
isQueryLoading: false,
balance: "",
address: "",
amount: "",
removeOnAccountAvailable: null,
removeOnViewingKeyCreated: null,
}),
mounted() {
this.removeOnAccountAvailable = onAccountAvailable(() => {
this.isConnected = true;
this.hasViewingKey();
});
this.removeOnViewingKeyCreated = onViewingKeyCreated(() => {
this.hasViewingKey();
});
},
unmounted() {
this.removeOnAccountAvailable();
this.removeOnViewingKeyCreated();
},
methods: {
async connect() {
await bootstrap();
},
hasViewingKey() {
const key = viewingKeyManager.get(sscrt.at);
return typeof key !== "undefined";
},
async createViewingKey() {
this.isMessageLoading = true;
try {
const result = await sscrt.createViewingKey();
if (result.isEmpty()) return;
const {
create_viewing_key: { key },
} = result.parse();
viewingKeyManager.add(sscrt, key);
} finally {
this.isMessageLoading = false;
}
},
async getBalance() {
if (!this.hasViewingKey()) return;
this.isQueryLoading = true;
try {
const {
balance: { amount: result },
} = await sscrt.getBalance();
const amount = coinConvert(result, 6, "human");
this.balance = amount;
} finally {
this.isQueryLoading = false;
}
},
async sendTokens(e) {
e.preventDefault();
if (!this.address || !this.amount) return;
this.isMessageLoading = true;
try {
const theAmount = coinConvert(this.amount, 6, "machine");
await sscrt.send(this.address, theAmount);
this.address = "";
this.amount = "";
} finally {
this.isMessageLoading = false;
}
},
},
};
</script>