Creating a Fungible Token Contract
In this tutorial we'll implement a Fungible Token.
To know what a fungible (and a non-fungible) token is, we recommend you to checkout Weil Tokens
The steps are slightly different depending on the language you are using, so make sure go chose the right language now.
- Rust
- Go
- AssemblyScript
- CPP
You have chosen Rust!
You have chosen Go!
You have chosen AssemblyScript!
You have chosen C++!
Preparations
This tutorial assumes that you have completed the Cross-Contract tutorial.
To start, create a new project. We'll name our example token Yutaka, as an homage to Japanese mathematician Yutaka Taniyama.
- Rust
- Go
- AssemblyScript
- CPP
cargo new yutaka --lib
cd yutaka
mkdir yutaka
cd yutaka
go mod init main
go mod tidy
mkdir contract
mkdir yutaka
cd yutaka
npm init
npm install --save-dev assemblyscript
npx asinit .
npm install assemblyscript-json json-as visitor-as --force
mkdir yutaka
cd yutaka
touch CMakeLists.txt
Prerequisites:
- Add an include folder in the root of your project, which contains all the required headers to work with the C++ SDK.
- Add the statically compiled library code (libweilsdk_static.a) in your project in the lib folder.
- You need to have emscripten installed.
Contract Specification
The token is defined by the following WIDL interface.
interface Yutaka {
query func name() -> string;
query func symbol() -> string;
query func decimals() -> u8;
query func details() -> tuple<string, string, u8>;
query func total_supply() -> uint;
query func balance_for(addr: string) -> result<uint, string>;
mutate func transfer(to_addr: string, amount: uint) -> result<(), string>;
mutate func approve(spender: string, amount: uint);
mutate func transfer_from(from_addr: string, to_addr: string, amount: uint) -> result<(), string>;
query func allowance(owner: string, spender: string) -> uint
}
This is the minimal interface any Fungible Token must implement in a WeilChain
to be compatible with our Wallet.
You could extend it for your own purposes, though, but we will not do this in this tutorial.
Server-Side Bindings
As usual, we need to create the server-side bindings and the contract skeleton.
- Rust
- Go
- AssemblyScript
- CPP
widl generate yutaka.widl server rust
This will have generated the bindings.rs
file, with the Applet skeleton.
Let's fill in the skeleton with the contract logic.
First, lets move the file to its final location.
cat bindings.rs > src/lib.rs
rm bindings.rs
widl generate yutaka.widl server go
This will have generated the contract.go
file, with the Applet skeleton.
Let's fill in the skeleton with the contract logic.
First, lets move the file to its final location.
mkdir contract
mv types.go exports.go contract.go contract
AssemblyScript SDK doesn't support binding generation yet. Please copy the file manually for now.
Create the file assembly/yutaka.ts
with the following content
@json
export class YutakaContractState {
inner: FungibleToken
constructor() {}
name(): string {}
symbol(): string {}
decimals(): u8 {}
details(): Tuple {}
totalSupply(): u64 {}
balanceFor(addr: string): Result<Box<u64>, WeilError> {}
transfer(to_addr: string, amount: u64): Result<Box<i32>, WeilError> {}
approve(spender: string, amount: u64): void {}
transferFrom(
from_addr: string,
to_addr: string,
amount: u64,
): Result<Box<i32>, WeilError> {}
allowance(owner: string, spender: string): u64 {}
}
Update the file assembly/index.ts
to contain the following code
export function init(): void {}
export function name(): void {}
export function symbol(): void {}
export function decimals(): void {}
export function details(): void {}
export function total_supply(): void {}
export function balance_for(): void {}
export function transfer(): void {}
export function approve(): void {}
export function transfer_from(): void {}
export function allowance(): void {}
export function yo(a: string): void {}
export function method_kind_data(): void {
const result: Map<string, string> = new Map<string, string>()
result.set('details', 'query')
result.set('name', 'query')
result.set('symbol', 'query')
result.set('decimals', 'query')
result.set('total_supply', 'query')
result.set('balance_for', 'query')
result.set('transfer', 'mutate')
result.set('approve', 'mutate')
result.set('transfer_from', 'mutate')
result.set('allowance', 'query')
Runtime.setOkResult(result)
}
export function method_kind(): void {
method_kind_data()
}
C++ SDK doesn't support binding generation yet. Please copy the following file manually for now.
This file contains the Applet skeleton. Let's fill in the skeleton with the contract logic.
#include "weilsdk/error.h"
#include "weilsdk/runtime.h"
#include "weilsdk/collections/map.hpp"
#include "weilsdk/ledger.h"
#include "weilsdk/weil_contracts/fungible.h"
#include "yutaka.hpp"
#include <iostream>
#include <string>
#include <map>
#include <vector>
extern "C" {
int __new(size_t len, unsigned char _id) {
void *ptr = weilsdk::Runtime::allocate(len);
return reinterpret_cast<int>(ptr);
}
void method_kind_data() {
std::map<std::string, std::string> method_kind_mapping;
method_kind_mapping["name"]= "query";
method_kind_mapping["symbol"]= "query";
method_kind_mapping["decimals"]= "query";
method_kind_mapping["details"]= "query";
method_kind_mapping["total_supply"]= "query";
method_kind_mapping["balance_for"]= "query";
method_kind_mapping["transfer"]= "mutate";
method_kind_mapping["approve"]= "mutate";
method_kind_mapping["transfer_from"]= "mutate";
method_kind_mapping["allowance"]= "query";
nlohmann::json json_object = method_kind_mapping;
std::string serialized_string = json_object.dump();
weilsdk::Runtime::setResult(serialized_string,0);
}
void init() {
weilcontracts::FungibleToken ft("Yutaka", "YTK", 100000);
weilcontracts::Yutaka yutaka_instance(ft);
std::pair<bool, std::string> result = yutaka_instance.inner.mint();
if(result.first){
nlohmann::json j;
to_json(j,yutaka_instance);
std::string stateString = j.dump();
weilsdk::WeilValue wv;
wv.new_with_state_and_ok_value(stateString, "Ok");
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {wv});
}
else{
weilsdk::MethodError me = weilsdk::MethodError("init",result.second);
weilsdk::Runtime::setStateAndResult(weilsdk::WeilError::FunctionReturnedWithError(me));
}
}
void name(){
std::string stateString = weilsdk::Runtime::state();
nlohmann::json j = nlohmann::json::parse(stateString);
from_json(j,yutaka_instance);
std::string result = yutaka_instance.getName();
weilsdk::Runtime::setResult(result,0);
}
void symbol(){
std::string stateString = weilsdk::Runtime::state();
nlohmann::json j = nlohmann::json::parse(stateString);
from_json(j,yutaka_instance);
std::string result = yutaka_instance.getSymbol();
weilsdk::Runtime::setResult(result,0);
}
void decimals(){
std::string stateString = weilsdk::Runtime::state();
nlohmann::json j = nlohmann::json::parse(stateString);
from_json(j,yutaka_instance);
uint8_t result = yutaka_instance.getDecimals();
weilsdk::Runtime::setResult(std::to_string(result),0);
}
void details(){
std::string stateString = weilsdk::Runtime::state();
nlohmann::json j = nlohmann::json::parse(stateString);
from_json(j,yutaka_instance);
std::tuple<std::string, std::string, uint8_t> result = yutaka_instance.getDetails();
nlohmann::json j1 = result;
weilsdk::Runtime::setResult(j1.dump(),0);
}
void total_supply(){
std::string stateString = weilsdk::Runtime::state();
nlohmann::json j = nlohmann::json::parse(stateString);
from_json(j,yutaka_instance);
int result = yutaka_instance.getTotalSupply();
weilsdk::Runtime::setResult(std::to_string(result),0);
}
void balance_for(){
std::pair<std::string, std::string> p = weilsdk::Runtime::stateAndArgs();
std::string raw_args = p.second;
nlohmann::json j = nlohmann::json::parse(raw_args);
if (j.is_discarded() || !j.contains("addr"))
{
weilsdk::MethodError me = weilsdk::MethodError("balance_for", "invalid_args");
weilsdk::Runtime::setResult(weilsdk::WeilError::MethodArgumentDeserializationError(me), 0);
return;
}
balanceForArgs args;
args = j.get<balanceForArgs>();
std::string stateString = p.first;
nlohmann::json j1 = nlohmann::json::parse(stateString);
from_json(j1,yutaka_instance);
int result = yutaka_instance.balanceFor(args.addr);
weilsdk::Runtime::setResult(std::to_string(result),0);
}
void transfer(){
std::pair<std::string, std::string> p = weilsdk::Runtime::stateAndArgs();
std::string raw_args = p.second;
nlohmann::json j = nlohmann::json::parse(raw_args);
if (j.is_discarded() || !j.contains("to_addr") || !j.contains("amount"))
{
weilsdk::MethodError me = weilsdk::MethodError("transfer", "invalid_args");
weilsdk::Runtime::setResult(weilsdk::WeilError::MethodArgumentDeserializationError(me), 0);
return;
}
transferArgs args;
args = j.get<transferArgs>();
std::string stateString = p.first;
nlohmann::json j1 = nlohmann::json::parse(stateString);
from_json(j1,yutaka_instance);
bool result = yutaka_instance.transfer(args.to_addr, args.amount).first;
if(result){
nlohmann::json j2 = yutaka_instance;
weilsdk::WeilValue wv;
wv.new_with_state_and_ok_value(j2.dump(), "Ok");
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {wv});
}
else{
weilsdk::MethodError me = weilsdk::MethodError("transfer","could not transfer");
std::string err = weilsdk::WeilError::FunctionReturnedWithError(me);
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {err});
}
}
void approve(){
std::pair<std::string, std::string> p = weilsdk::Runtime::stateAndArgs();
std::string raw_args = p.second;
nlohmann::json j = nlohmann::json::parse(raw_args);
if (j.is_discarded() || !j.contains("spender") || !j.contains("amount"))
{
weilsdk::MethodError me = weilsdk::MethodError("approve", "invalid_args");
weilsdk::Runtime::setResult(weilsdk::WeilError::MethodArgumentDeserializationError(me), 0);
return;
}
approveArgs args;
args = j.get<approveArgs>();
std::string stateString = p.first;
nlohmann::json j1 = nlohmann::json::parse(stateString);
from_json(j1,yutaka_instance);
yutaka_instance.approve(args.spender, args.amount);
nlohmann::json j2 = yutaka_instance;
weilsdk::WeilValue wv;
wv.new_with_state_and_ok_value(j2.dump(), "Ok");
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {wv});
}
void transfer_from(){
std::pair<std::string, std::string> p = weilsdk::Runtime::stateAndArgs();
std::string raw_args = p.second;
nlohmann::json j = nlohmann::json::parse(raw_args);
if (j.is_discarded() || !j.contains("from_addr") || !j.contains("to_addr") || !j.contains("amount"))
{
weilsdk::MethodError me = weilsdk::MethodError("transfer_from", "invalid_args");
weilsdk::Runtime::setResult(weilsdk::WeilError::MethodArgumentDeserializationError(me), 0);
return;
}
transferFromArgs args;
args = j.get<transferFromArgs>();
std::string stateString = p.first;
nlohmann::json j1 = nlohmann::json::parse(stateString);
from_json(j1,yutaka_instance);
bool result = yutaka_instance.transferFrom(args.from_addr, args.to_addr, args.amount).first;
if(result){
nlohmann::json j2 = yutaka_instance;
weilsdk::WeilValue wv;
wv.new_with_state_and_ok_value(j2.dump(), "Ok");
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {wv});
}
else{
weilsdk::MethodError me = weilsdk::MethodError("transfer_from","could not transfer_from");
std::string err = weilsdk::WeilError::FunctionReturnedWithError(me);
weilsdk::Runtime::setStateAndResult(std::variant<weilsdk::WeilValue,weilsdk::WeilError> {err});
}
}
void allowance(){
std::pair<std::string, std::string> p = weilsdk::Runtime::stateAndArgs();
std::string raw_args = p.second;
nlohmann::json j = nlohmann::json::parse(raw_args);
if (j.is_discarded() || !j.contains("spender") || !j.contains("owner"))
{
weilsdk::MethodError me = weilsdk::MethodError("allowance", "invalid_args");
weilsdk::Runtime::setResult(weilsdk::WeilError::MethodArgumentDeserializationError(me), 0);
return;
}
allowanceForArgs args;
args = j.get<allowanceForArgs>();
std::string stateString = p.first;
nlohmann::json j1 = nlohmann::json::parse(stateString);
from_json(j1,yutaka_instance);
int result = yutaka_instance.allowance(args.owner, args.spender);
weilsdk::Runtime::setResult(std::to_string(result),0);
}
}
Filling in the Logic
Contract State
The Yutaka contract has an inner field of type Fungible token, to which most operations are delegated.
- Rust
- Go
- AssemblyScript
- CPP
...
#[derive(Serialize, Deserialize, WeilType)]
pub struct YutakaContractState {
inner: FungibleToken,
}
...
...
type YutakaContractState struct {
Inner fungible.FungibleToken
}...
@json
export class YutakaContractState {
inner: FungibleToken
}
#include "weilsdk/weil_contracts/fungible.h"
#include "yutaka.hpp"
namespace weilcontracts {
class Yutaka {
private:
public:
FungibleToken inner;
Yutaka(const FungibleToken &inner_) : inner(inner_) {}
Yutaka();
}
};
Contract Initialization
The constructor is responsible for initializing the inner Fungible token. Initializing it requires 3 parameters:
- name: the name of the token
- symbol: the symbol of the token
- total supply: the total number of this token for ever to exist.
For our example, we use "Yutaka" as name, "YTK" as symbol, and 100_000 as total supply.
- Rust
- Go
- AssemblyScript
- CPP
#[constructor]
fn new() -> Result<Self, String>
where
Self: Sized,
{
let total_supply = 100000;
let mut yutaka_token = YutakaContractState {
inner: FungibleToken::new("Yutaka".to_string(), "YTK".to_string(), total_supply),
};
yutaka_token.inner.mint().map_err(|err| err.to_string())?;
Ok(yutaka_token)
}
func NewYutakaContractState() (*YutakaContractState, error) {
token := YutakaContractState{
Inner: *fungible.NewFungibleToken("Yutaka", "YTK", total_supply),
}
err := token.Inner.Mint()
if err != nil {
return nil, err
}
return &token, nil
}
Updated yutaka.ts
file
@json
export class YutakaContractState {
inner: FungibleToken
constructor() {
let totalSupply: u64 = 100000
this.inner = new FungibleToken('Yutaka', 'YTK', totalSupply)
this.inner.mint()
}
...
}
Updated index.ts
file
export function init(): void {
const state = new YutakaContractState()
Runtime.setState(state)
Runtime.setOkResult(state)
}
Yutaka::Yutaka()
: inner("Yutaka", "YTK", 100000)
{}
We also need to construct serialization and deserialization methods for a Yutaka instance. To achieve this, we define two additional functions in the Yutaka class
const FungibleToken& getInner() const { return inner; }
void setInner(const FungibleToken& token) { inner = token; }
Now we will use these two functions in the serialization and deserialization of a Yutaka instance.
inline void to_json(nlohmann::json &j, const Yutaka &yutaka) {
j = nlohmann::json{
{"inner", yutaka.getInner()}
};
}
inline void from_json(const nlohmann::json &j, Yutaka &yutaka) {
std::string name = j.at("inner").at("name").get<std::string>();
std::string symbol = j.at("inner").at("symbol").get<std::string>();
int totalSupply = j.at("inner").at("totalSupply").get<int>();
FungibleToken token(name, symbol, totalSupply);
yutaka.setInner(token);
}
Basic Methods
The contract provides methods to retrieve data related to the inner Fungible Token, as follows:
- Rust
- Go
- AssemblyScript
- CPP
#[query]
async fn name(&self) -> String {
self.inner.name()
}
#[query]
async fn symbol(&self) -> String {
self.inner.symbol()
}
#[query]
async fn decimals(&self) -> u8 {
18
}
#[query]
async fn details(&self) -> (String, String, u8) {
(self.name().await, self.symbol().await, self.decimals().await)
}
#[query]
async fn total_supply(&self) -> usize {
self.inner.total_supply()
}
// query
func (obj *YutakaContractState) Name() string {
result := obj.Inner.GetName()
return result
}
// query
func (obj *YutakaContractState) Symbol() string {
result := obj.Inner.GetSymbol()
return result
}
// query
func (obj *YutakaContractState) Decimals() uint8 {
result := obj.Inner.GetDecimals()
return result
}
// query
func (obj *YutakaContractState) Details() types.Tuple3[string, string, uint8] {
result := types.Tuple3[string, string, uint8]{
F0: obj.Inner.GetName(),
F1: obj.Inner.GetSymbol(),
F2: obj.Inner.GetDecimals(),
}
return result
}
// query
func (obj *YutakaContractState) TotalSupply() uint64 {
result := obj.Inner.GetTotalSupply()
return result
}
yutaka.ts
file
name(): string {
return this.inner.name
}
symbol(): string {
return this.inner.symbol
}
decimals(): u8 {
return 18
}
details(): Tuple {
return this.inner.getDetails()
}
totalSupply(): u64 {
return this.inner.totalSupply
}
balanceFor(addr: string): Result<Box<u64>, WeilError> {
return this.inner.balanceFor(addr)
}
transfer(to_addr: string, amount: u64): Result<Box<i32>, WeilError> {
return this.inner.transfer(to_addr, amount)
}
approve(spender: string, amount: u64): void {
this.inner.approve(spender, amount)
}
transferFrom(
from_addr: string,
to_addr: string,
amount: u64,
): Result<Box<i32>, WeilError> {
return this.inner.transferFrom(from_addr, to_addr, amount)
}
allowance(owner: string, spender: string): u64 {
return this.inner.allowance(owner, spender)
}
index.ts
file
export function name(): void {
const state = Runtime.state<YutakaContractState>()
Runtime.setOkResult(state.name())
}
export function symbol(): void {
const state = Runtime.state<YutakaContractState>()
Runtime.setOkResult(state.symbol())
}
export function decimals(): void {
const state = Runtime.state<YutakaContractState>()
Runtime.setOkResult(state.decimals())
}
export function details(): void {
const state: YutakaContractState = Runtime.state<YutakaContractState>()
Runtime.setOkResult(state.details().toJSON())
}
export function total_supply(): void {
const state: YutakaContractState = Runtime.state<YutakaContractState>()
Runtime.setOkResult(state.totalSupply())
}
export function balance_for(): void {
const stateAndArgs = Runtime.stateAndArgs<YutakaContractState, BalanceForArgs>()
const state = (stateAndArgs.elements[0] as JSONWrapper<YutakaContractState>).inner;
const args = (stateAndArgs.elements[1] as JSONWrapper<BalanceForArgs>).inner;
if (!args.addr) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'balance_for',
err_msg: `field "addr" must be provided`,
}),
)
return
}
const result = state.balanceFor(args.addr)
if (result.isOk()) {
Runtime.setOkResult(result.tryValue().value)
} else {
Runtime.setResult(result)
}
}
export function transfer(): void {
const stateAndArgs = Runtime.stateAndArgs<YutakaContractState, TransferArgs>()
const state = (stateAndArgs.elements[0] as JSONWrapper<YutakaContractState>).inner;
const args = (stateAndArgs.elements[1] as JSONWrapper<TransferArgs>).inner;
if (!args.to_addr) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'transfer',
err_msg: `field "to_addr" must be provided`,
}),
)
return
}
if (args.amount === 0) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'transfer',
err_msg: `field "amount" must be provided`,
}),
)
return
}
const res = state.transfer(args.to_addr, args.amount);
if(res.isOk()){
const weilValue = WeilValue.newWithStateAndOkValue(state, res.tryValue());
const result = Result.Ok<WeilValue<YutakaContractState,Box<i32>>, WeilError>(weilValue);
Runtime.setStateAndResult<YutakaContractState,Box<i32>>(result);
}else{
const error = res.tryError();
const result = Result.Err<WeilValue<YutakaContractState,Box<i32>>, WeilError>(error);
Runtime.setStateAndResult<YutakaContractState,Box<i32>>(result);
}
}
export function approve(): void {
const stateAndArgs = Runtime.stateAndArgs<YutakaContractState, ApproveArgs>()
const state = (stateAndArgs.elements[0] as JSONWrapper<YutakaContractState>).inner;
const args = (stateAndArgs.elements[1] as JSONWrapper<ApproveArgs>).inner;
if (!args.spender) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'approve',
err_msg: `field "spender" must be provided`,
}),
)
return
}
if (args.value === 0) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'approve',
err_msg: `field "value" must be provided`,
}),
)
return
}
state.approve(args.spender, args.value)
const weilValue = WeilValue.newWithStateAndOkValue(state, "ok");
const result = Result.Ok<WeilValue<YutakaContractState,string>, WeilError>(weilValue);
Runtime.setStateAndResult<YutakaContractState,string>(result);
}
export function transfer_from(): void {
const stateAndArgs = Runtime.stateAndArgs<YutakaContractState, TransferFromArgs>()
const state = (stateAndArgs.elements[0] as JSONWrapper<YutakaContractState>).inner;
const args = (stateAndArgs.elements[1] as JSONWrapper<TransferFromArgs>).inner;
if (!args.from_addr) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'transfer_from',
err_msg: `field "from_addr" must be provided`,
}),
)
return
}
if (!args.to_addr) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'transfer_from',
err_msg: `field "to_addr" must be provided`,
}),
)
return
}
if (args.value === 0) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'transfer_from',
err_msg: `field "value" must be provided`,
}),
)
return
}
const res = state.transferFrom(
args.from_addr,
args.to_addr,
<usize>args.value,
)
const weilValue = WeilValue.newWithStateAndOkValue(state, res.tryValue().value);
const result = Result.Ok<WeilValue<YutakaContractState,i32>, WeilError>(weilValue);
Runtime.setStateAndResult<YutakaContractState,i32>(result);
}
export function allowance(): void {
const stateAndArgs = Runtime.stateAndArgs<YutakaContractState, AllowanceArgs>()
const state = (stateAndArgs.elements[0] as JSONWrapper<YutakaContractState>).inner;
const args = (stateAndArgs.elements[1] as JSONWrapper<AllowanceArgs>).inner;
if (!args.owner) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'allowance',
err_msg: `field "owner" must be provided`,
}),
)
return
}
if (!args.spender) {
Runtime.setErrorResult(
WeilError.MethodArgumentDeserializationError({
method_name: 'allowance',
err_msg: `field "spender" must be provided`,
}),
)
return
}
Runtime.setOkResult(state.allowance(args.owner, args.spender))
}
std::string Yutaka::getName() {
return inner.getName();
}
std::string Yutaka::getSymbol() {
return inner.getSymbol();
}
uint8_t Yutaka::getDecimals() {
return inner.getDecimals();
}
std::tuple<std::string, std::string, uint8_t> Yutaka::getDetails() {
return std::make_tuple(inner.getName(), inner.getSymbol(), inner.getDecimals());
}
int Yutaka::getTotalSupply() {
return inner.getTotalSupply();
}
Query Methods
Other query methods are used to get retrieve information based on the current state of the contract.
- Rust
- Go
- AssemblyScript
- CPP
#[query]
async fn balance_for(&self, addr: String) -> Result<usize, String> {
self.inner.balance_for(addr).map_err(|err| err.to_string())
}
#[query]
async fn allowance(&self, owner: String, spender: String) -> usize {
self.inner.allowance(owner, spender)
}
// query
func (obj *YutakaContractState) BalanceFor(addr string) (*uint64, error) {
result, err := obj.Inner.BalanceFor(addr)
if err != nil {
return nil, err
}
return result, nil
}
// query
func (obj *YutakaContractState) Allowance(owner string, spender string) uint64 {
result := obj.Inner.Allowance(owner, spender)
return result
}
balanceFor(addr: string): Result<Box<u64>, WeilError> {
return this.inner.balanceFor(addr)
}
allowance(owner: string, spender: string): u64 {
return this.inner.allowance(owner, spender)
}
int Yutaka::balanceFor(const std::string &addr) {
return inner.balanceFor(addr);
}
int Yutaka::allowance(const std::string &owner, const std::string &spender){
return inner.getAllowance(owner, spender);
}
Mutation Methods
These methods are used to modify the current state of the contract.
- Rust
- Go
- AssemblyScript
- CPP
#[mutate]
async fn transfer(&mut self, to_addr: String, amount: usize) -> Result<(), String> {
self.inner
.transfer(to_addr, amount)
.map_err(|err| err.to_string())
}
#[mutate]
async fn approve(&mut self, spender: String, amount: usize) {
self.inner.approve(spender, amount)
}
#[mutate]
async fn transfer_from(
&mut self,
from_addr: String,
to_addr: String,
amount: usize,
) -> Result<(), String> {
self.inner
.transfer_from(from_addr, to_addr, amount)
.map_err(|err| err.to_string())
}
// mutate
func (obj *YutakaContractState) Transfer(toAddr string, amount uint64) error {
err := obj.Inner.Transfer(toAddr, amount)
return err
}
// mutate
func (obj *YutakaContractState) Approve(spender string, amount uint64) {
obj.Inner.Approve(spender, amount)
}
// mutate
func (obj *YutakaContractState) TransferFrom(fromAddr string, toAddr string, amount uint64) error {
err := obj.Inner.TransferFrom(fromAddr, toAddr, amount)
return err
}
transfer(to_addr: string, amount: u64): Result<Box<i32>, WeilError> {
return this.inner.transfer(to_addr, amount)
}
approve(spender: string, amount: u64): void {
this.inner.approve(spender, amount)
}
transferFrom(
from_addr: string,
to_addr: string,
amount: u64,
): Result<Box<i32>, WeilError> {
return this.inner.transferFrom(from_addr, to_addr, amount)
}
std::pair<bool, std::string> Yutaka::transfer(const std::string &toAddr, int amount) {
return inner.transfer(toAddr, amount);
}
void Yutaka::approve(const std::string &spender, int amount) {
inner.approve(spender, amount);
}
std::pair<bool, std::string> Yutaka::transferFrom(const std::string &fromAddr, const std::string &toAddr, int amount) {
return inner.transferFrom(fromAddr, toAddr, amount);
}
You now have all pieces in place. Go ahead, build the contract and deploy it. Then follow the WebWallet tutorial to see and operate your tokens.