From 1f8ccdba16d023b76ecb21f072f2e1667e87362f Mon Sep 17 00:00:00 2001 From: Jeroen Peeters Date: Wed, 22 Aug 2018 18:49:30 +0200 Subject: [PATCH] chore: move token example to nep5 as package name (CityOfZion/neo-storm#19) * feat: add token example * feat: code splitted package * feat: use updated apis * chore: change token namespace to nep5 * chore: add transfer event and readme updates Imported from CityOfZion/neo-storm (63ec2d7dc23a60f128a8b383ceda1eaa15d919c1). --- examples/token/token.go | 6 +- examples/token/vendor/nep5/nep5.go | 112 +++++++++++++++++++++++++++++ interop/runtime/runtime.go | 4 +- pkg/vm/compiler/compiler_test.go | 2 +- 4 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 examples/token/vendor/nep5/nep5.go diff --git a/examples/token/token.go b/examples/token/token.go index 6505ce454..73b3ffba7 100644 --- a/examples/token/token.go +++ b/examples/token/token.go @@ -1,7 +1,7 @@ package token_contract import ( - "token" + "nep5" "github.com/CityOfZion/neo-storm/interop/storage" "github.com/CityOfZion/neo-storm/interop/util" @@ -15,8 +15,8 @@ const ( var owner = util.FromAddress("AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y") // CreateToken initializes the Token Interface for the Smart Contract to operate with -func CreateToken() token.Token { - return token.Token{ +func CreateToken() nep5.Token { + return nep5.Token{ Name: "Awesome NEO Token", Symbol: "ANT", Decimals: decimals, diff --git a/examples/token/vendor/nep5/nep5.go b/examples/token/vendor/nep5/nep5.go new file mode 100644 index 000000000..301ff6117 --- /dev/null +++ b/examples/token/vendor/nep5/nep5.go @@ -0,0 +1,112 @@ +package nep5 + +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 +} + +// 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) + runtime.Notify("transfer", 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 +} diff --git a/interop/runtime/runtime.go b/interop/runtime/runtime.go index 9f27ec001..40df58b17 100644 --- a/interop/runtime/runtime.go +++ b/interop/runtime/runtime.go @@ -12,7 +12,7 @@ func CheckWitness(hash []byte) bool { func Log(message string) {} // Notify an event to the VM. -func Notify(arg interface{}) int { +func Notify(arg ...interface{}) int { return 0 } @@ -42,7 +42,7 @@ func Serialize(item interface{}) []byte { return nil } -// Deserializes an item from a bytearray. +// Deserialize an item from a bytearray. func Deserialize(b []byte) interface{} { return nil } diff --git a/pkg/vm/compiler/compiler_test.go b/pkg/vm/compiler/compiler_test.go index ac17fcc25..a42bd418d 100644 --- a/pkg/vm/compiler/compiler_test.go +++ b/pkg/vm/compiler/compiler_test.go @@ -23,7 +23,7 @@ func TestExamplesFolder(t *testing.T) { t.Fatal(err) } if len(infos) > 1 { - t.Fatal("detected smart contract folder with more then 1 contract file") + t.Fatal("detected smart contract folder with more than 1 contract file") } if len(infos) == 0 { t.Fatal("detected smart contract folder with no contract in it")