Skip to content

Instantly share code, notes, and snippets.

@jim-toth
Last active October 2, 2023 20:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jim-toth/07bf23793f65e8970271398f7674ddcb to your computer and use it in GitHub Desktop.
Save jim-toth/07bf23793f65e8970271398f7674ddcb to your computer and use it in GitHub Desktop.
SWS-2 SmartWeave Token

SWS-2: SmartWeave Token

Status: Published

Version: 1.1.0

Abstract

This document describes the standard interface for tokenized asset SmartWeave Contracts. The SmartWeave Token Spec refers to a SmartWeave Contract that can grant ownership properties to any arweave asset when it is deployed, effectively turning it into a fungible token. However, it's important to note that the fundamental characteristics of an atomic token alone do not allow for unrestricted trading without the presence of an interop protocol, such as the Foreign Call Protocol.

Motivation

The Permaweb community requires a transparent and straightforward approach to establish clear semantics for tradable contracts. The SmartWeave Token serves as the designated specification, which has already been adopted as the standard practice within the ecosystem for a considerable period of time. This document aims to formalize and document this practice to ensure clarity and transparency.

Specification

The contract specification consists of state properties and contract functions. The three primary state properties are name, ticker, and balances. The contract functions include balance and transfer. The choice between a fungible or non-fungible token is left to the creator's discretion. While the contract must include these elements, it may also incorporate additional state properties or functions as desired. It is frequently observed that the SWT is often combined with the Foreign Call Protocol spec for enhanced functionality.

Standard Interface

balanceOf

balanceOf(target: Address) => { target: Address, balance: number }
  • Returns the current balance of a target address

transfer

transfer(target: Address, qty: number) => void
  • Transfers qty of tokens to target address
  • SHOULD THROW if the caller does not have enough balance to transfer.

name

name() => string | undefined
  • Returns the name of the SmartWeave Token
  • MAY be ommitted if the contract state includes a property name of type string | undefined.

ticker

ticker() => string | undefined
  • Returns the ticker of the SmartWeave Token
  • MAY be ommitted if the contract state includes a property ticker of type string | undefined.

decimals

decimals() => number
  • Returns the number of decimal places the token amount should display.
  • MUST return an integer
  • MAY be ommitted if the contract state includes a property decimals of type number and is an integer.

Appendix

Interfaces & Types

type Address = string

interface State {
  name: string
  ticker: string
  decimals: number // Integer
  balances: Record<Address, number>
}

interface BalanceOf {
  target: Address
}

interface Transfer {
  qty: number
  target: Address
}

Example Implementation

State

Name Type Description
name string The name of the token
ticker string The ticker symbol of the token
decimals number the number (integer) of decimal places the token amount should display.
balances Record<Address, Number> Map of addresses to their numeric balance

balanceOf

/**
  @param {State} state
  @param {BalanceOf} action
  @returns {Result} The current balance of the target address
*/
function balance(state: State, action: BalanceOf) {
  return {
    result: {
      target: action.input.target,
      balance: state.balances[action.input.target] || 0
    }
  }
}

transfer

/**
  @param {State} state
  @param {Transfer} action
  @returns {State}
*/
function transfer(state: State, action: Transfer) {
  if (!action.input.qty || typeof action.input.qty !== "number") {
    throw new ContractError("qty is not defined or is not a number")
  }

  if (action.caller === action.input.target) {
    throw new ContractError("target cannot be caller")
  }

  if (!state.balances[action.input.target]) {
    state.balances[action.input.target] = 0
  }
  
  if (!state.balances[action.caller]) {
    state.balances[action.caller] = 0
  }

  if (state.balances[action.caller] < action.input.qty) {
    throw new ContractError("not enough balance to transfer")
  }
  
  state.balances[action.caller] -= action.input.qty
  state.balances[action.input.target] += action.input.qty

  return { state }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment