forked from TrueCloudLab/neoneo-go
Token Example (CityOfZion/neo-storm#12)
* feat: add token example * feat: code splitted package * feat: use updated apis Imported from CityOfZion/neo-storm (e2bab450d7355b559ae2d70a87f557e8a6dbfff6).
This commit is contained in:
parent
8bfaed0e4b
commit
b3037cd598
3 changed files with 186 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,6 +13,7 @@
|
||||||
# Added by CoZ developers
|
# Added by CoZ developers
|
||||||
vendor/
|
vendor/
|
||||||
bin/
|
bin/
|
||||||
|
!examples/**/vendor
|
||||||
|
|
||||||
# text editors
|
# text editors
|
||||||
# vscode
|
# vscode
|
||||||
|
|
70
examples/token/token.go
Normal file
70
examples/token/token.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package token_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"token"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-storm/interop/storage"
|
||||||
|
"github.com/CityOfZion/neo-storm/interop/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
decimals = 8
|
||||||
|
multiplier = 100000000
|
||||||
|
)
|
||||||
|
|
||||||
|
var owner = util.FromAddress("AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y")
|
||||||
|
|
||||||
|
// CreateToken initializes the Token Interface for the Smart Contract to operate with
|
||||||
|
func CreateToken() token.Token {
|
||||||
|
return token.Token{
|
||||||
|
Name: "Awesome NEO Token",
|
||||||
|
Symbol: "ANT",
|
||||||
|
Decimals: decimals,
|
||||||
|
Owner: owner,
|
||||||
|
TotalSupply: 11000000 * multiplier,
|
||||||
|
CirculationKey: "TokenCirculation",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function = contract entry
|
||||||
|
func Main(operation string, args []interface{}) interface{} {
|
||||||
|
token := CreateToken()
|
||||||
|
|
||||||
|
if operation == "name" {
|
||||||
|
return token.Name
|
||||||
|
}
|
||||||
|
if operation == "symbol" {
|
||||||
|
return token.Symbol
|
||||||
|
}
|
||||||
|
if operation == "decimals" {
|
||||||
|
return token.Decimals
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following operations need ctx
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
if operation == "totalSupply" {
|
||||||
|
return token.GetSupply(ctx)
|
||||||
|
}
|
||||||
|
if operation == "balanceOf" {
|
||||||
|
hodler := args[0].([]byte)
|
||||||
|
return token.BalanceOf(ctx, hodler)
|
||||||
|
}
|
||||||
|
if operation == "transfer" && CheckArgs(args, 3) {
|
||||||
|
from := args[0].([]byte)
|
||||||
|
to := args[1].([]byte)
|
||||||
|
amount := args[2].(int)
|
||||||
|
return token.Transfer(ctx, from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckArgs checks args array against a length indicator
|
||||||
|
func CheckArgs(args []interface{}, length int) bool {
|
||||||
|
if len(args) == length {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
115
examples/token/vendor/token/token.go
vendored
Normal file
115
examples/token/vendor/token/token.go
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-storm/interop/engine"
|
||||||
|
"github.com/CityOfZion/neo-storm/interop/runtime"
|
||||||
|
"github.com/CityOfZion/neo-storm/interop/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token holds all token info
|
||||||
|
type Token struct {
|
||||||
|
// Token name
|
||||||
|
Name string
|
||||||
|
// Ticker symbol
|
||||||
|
Symbol string
|
||||||
|
// Amount of decimals
|
||||||
|
Decimals int
|
||||||
|
// Token owner address
|
||||||
|
Owner []byte
|
||||||
|
// Total tokens * multiplier
|
||||||
|
TotalSupply int
|
||||||
|
// Storage key for circulation value
|
||||||
|
CirculationKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Transfer event
|
||||||
|
// DoTransfer := action.RegisterAction("transfer", "from", "to", "amount")
|
||||||
|
|
||||||
|
// GetSupply gets the token totalSupply value from VM storage
|
||||||
|
func (t Token) GetSupply(ctx storage.Context) interface{} {
|
||||||
|
return storage.Get(ctx, t.CirculationKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceOf gets the token balance of a specific address
|
||||||
|
func (t Token) BalanceOf(ctx storage.Context, hodler []byte) interface{} {
|
||||||
|
return storage.Get(ctx, hodler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer token from one user to another
|
||||||
|
func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int) bool {
|
||||||
|
amountFrom := t.CanTransfer(ctx, from, to, amount)
|
||||||
|
if amountFrom == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if amountFrom == 0 {
|
||||||
|
storage.Delete(ctx, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
if amountFrom > 0 {
|
||||||
|
diff := amountFrom - amount
|
||||||
|
storage.Put(ctx, from, diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
amountTo := storage.Get(ctx, to).(int)
|
||||||
|
totalAmountTo := amountTo + amount
|
||||||
|
storage.Put(ctx, to, totalAmountTo)
|
||||||
|
// DoTransfer(from, to, amount)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanTransfer returns the amount it can transfer
|
||||||
|
func (t Token) CanTransfer(ctx storage.Context, from []byte, to []byte, amount int) int {
|
||||||
|
if len(to) != 20 && !IsUsableAddress(from) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
amountFrom := storage.Get(ctx, from).(int)
|
||||||
|
if amountFrom < amount {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell Transfer the result is equal - special case since it uses Delete
|
||||||
|
if amountFrom == amount {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// return amountFrom value back to Transfer, reduces extra Get
|
||||||
|
return amountFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUsableAddress checks if the sender is either the correct NEO address or SC address
|
||||||
|
func IsUsableAddress(addr []byte) bool {
|
||||||
|
if len(addr) == 20 {
|
||||||
|
|
||||||
|
if runtime.CheckWitness(addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a smart contract is calling scripthash
|
||||||
|
callingScriptHash := engine.GetCallingScriptHash()
|
||||||
|
if EqualAddresses(callingScriptHash, addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualAddresses compares two addresses if they're equal
|
||||||
|
// also returns false if one of the two - or both - aren't actual addresses
|
||||||
|
func EqualAddresses(a []byte, b []byte) bool {
|
||||||
|
aLen := len(a)
|
||||||
|
bLen := len(b)
|
||||||
|
if aLen != bLen || aLen != 20 || bLen != 20 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < aLen; i++ {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
Loading…
Reference in a new issue