examples: drop token-sale contract
It makes zero sense, it's not what it says it is. AmountPerNEO/MaxExchangeLimitRound are not used at all to affect minting and allowance/transferFrom features are largely obsolete. We have proper minting implemented in NFT contracts and there is a nice NEP-17 available also.
This commit is contained in:
parent
a5979f6d64
commit
26c3a6c161
5 changed files with 0 additions and 300 deletions
|
@ -33,7 +33,6 @@ See the table below for the detailed examples description.
|
||||||
| [storage](storage) | The contract implements API for basic operations with a contract storage. It shows hos to use `storage` interop package. See the `storage` [package documentation](../pkg/interop/storage/storage.go). |
|
| [storage](storage) | The contract implements API for basic operations with a contract storage. It shows hos to use `storage` interop package. See the `storage` [package documentation](../pkg/interop/storage/storage.go). |
|
||||||
| [timer](timer) | The idea of the contract is to count `tick` method invocations and destroy itself after the third invocation. It shows how to use `contract.Call` interop function to call, update (migrate) and destroy the contract. Please, refer to the `contract.Call` [function documentation](../pkg/interop/contract/contract.go) |
|
| [timer](timer) | The idea of the contract is to count `tick` method invocations and destroy itself after the third invocation. It shows how to use `contract.Call` interop function to call, update (migrate) and destroy the contract. Please, refer to the `contract.Call` [function documentation](../pkg/interop/contract/contract.go) |
|
||||||
| [token](token) | This contract implements NEP-17 token standard (like NEO and GAS tokens) with all required methods and operations. See the NEP-17 token standard [specification](https://github.com/neo-project/proposals/pull/126) for details. |
|
| [token](token) | This contract implements NEP-17 token standard (like NEO and GAS tokens) with all required methods and operations. See the NEP-17 token standard [specification](https://github.com/neo-project/proposals/pull/126) for details. |
|
||||||
| [token-sale](token-sale) | The contract represents a token with `allowance`. It means that the token owner should approve token withdrawing before the transfer. The contract demonstrates how interop packages can be combined to work together. |
|
|
||||||
|
|
||||||
## Compile
|
## Compile
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
module github.com/nspcc-dev/neo-go/examples/token-sale
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321144137-d5a9af5860af
|
|
|
@ -1,2 +0,0 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321144137-d5a9af5860af h1:QO3pU/jSYyX3EHBX8BPO01oRkVhGBXPrQaQEhn+4fv8=
|
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220321144137-d5a9af5860af/go.mod h1:QBE0I30F2kOAISNpT5oks82yF4wkkUq3SCfI3Hqgx/Y=
|
|
|
@ -1,279 +0,0 @@
|
||||||
package tokensale
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
decimals = 8
|
|
||||||
multiplier = decimals * 10
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
|
|
||||||
trigger byte
|
|
||||||
token TokenConfig
|
|
||||||
ctx storage.Context
|
|
||||||
)
|
|
||||||
|
|
||||||
// TokenConfig holds information about the token we want to use for the sale.
|
|
||||||
type TokenConfig struct {
|
|
||||||
// Name of the token.
|
|
||||||
Name string
|
|
||||||
// 3 letter abbreviation of the token.
|
|
||||||
Symbol string
|
|
||||||
// How decimals this token will have.
|
|
||||||
Decimals int
|
|
||||||
// Address of the token owner. This is the Uint160 hash.
|
|
||||||
Owner []byte
|
|
||||||
// The total amount of tokens created. Notice that we need to multiply the
|
|
||||||
// amount by 100000000. (10^8)
|
|
||||||
TotalSupply int
|
|
||||||
// Initial amount is number of tokens that are available for the token sale.
|
|
||||||
InitialAmount int
|
|
||||||
// How many NEO will be worth 1 token. For example:
|
|
||||||
// Lets say 1 euro per token, where 1 NEO is 60 euro. This means buyers
|
|
||||||
// will get (60 * 10^8) tokens for 1 NEO.
|
|
||||||
AmountPerNEO int
|
|
||||||
// How many Gas will be worth 1 token. This is the same calculation as
|
|
||||||
// for the AmountPerNEO, except Gas price will have a different value.
|
|
||||||
AmountPerGas int
|
|
||||||
// The maximum amount you can mint in the limited round. For example:
|
|
||||||
// 500 NEO/buyer * 60 tokens/NEO * 10^8
|
|
||||||
MaxExchangeLimitRound int
|
|
||||||
// When to start the token sale.
|
|
||||||
SaleStart int
|
|
||||||
// When to end the initial limited round if there is one. For example:
|
|
||||||
// SaleStart + 10000
|
|
||||||
LimitRoundEnd int
|
|
||||||
// The prefix used to store how many tokens there are in circulation.
|
|
||||||
CirculationKey []byte
|
|
||||||
// The prefix used to store how many tokens there are in the limited round.
|
|
||||||
LimitRoundKey []byte
|
|
||||||
// The prefix used to store the addresses that are registered with KYC.
|
|
||||||
KYCKey []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// newTokenConfig returns the initialized TokenConfig.
|
|
||||||
func newTokenConfig() TokenConfig {
|
|
||||||
return TokenConfig{
|
|
||||||
Name: "My awesome token",
|
|
||||||
Symbol: "MAT",
|
|
||||||
Decimals: decimals,
|
|
||||||
Owner: owner,
|
|
||||||
TotalSupply: 10000000 * multiplier,
|
|
||||||
InitialAmount: 5000000 * multiplier,
|
|
||||||
AmountPerNEO: 60 * multiplier,
|
|
||||||
AmountPerGas: 40 * multiplier,
|
|
||||||
MaxExchangeLimitRound: 500 * 60 * multiplier,
|
|
||||||
SaleStart: 75500,
|
|
||||||
LimitRoundEnd: 75500 + 10000,
|
|
||||||
CirculationKey: []byte("in_circulation"),
|
|
||||||
LimitRoundKey: []byte("r1"),
|
|
||||||
KYCKey: []byte("kyc_ok"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getIntFromDB is a helper that checks for nil result of storage.Get and returns
|
|
||||||
// zero as the default value.
|
|
||||||
func getIntFromDB(ctx storage.Context, key []byte) int {
|
|
||||||
var res int
|
|
||||||
val := storage.Get(ctx, key)
|
|
||||||
if val != nil {
|
|
||||||
res = val.(int)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
// InCirculation returns the amount of total tokens that are in circulation.
|
|
||||||
func InCirculation() int {
|
|
||||||
return getIntFromDB(ctx, token.CirculationKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addToCirculation sets the given amount as "in circulation" in the storage.
|
|
||||||
func addToCirculation(amount int) bool {
|
|
||||||
if amount < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
supply := getIntFromDB(ctx, token.CirculationKey)
|
|
||||||
supply += amount
|
|
||||||
if supply > token.TotalSupply {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
storage.Put(ctx, token.CirculationKey, supply)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// AvailableAmount returns the total amount of available tokens left
|
|
||||||
// to be distributed.
|
|
||||||
func AvailableAmount() int {
|
|
||||||
inCirc := getIntFromDB(ctx, token.CirculationKey)
|
|
||||||
return token.TotalSupply - inCirc
|
|
||||||
}
|
|
||||||
|
|
||||||
// init initializes runtime trigger, TokenConfig and storage context before any
|
|
||||||
// other contract method is called
|
|
||||||
func init() {
|
|
||||||
trigger = runtime.GetTrigger()
|
|
||||||
token = newTokenConfig()
|
|
||||||
ctx = storage.GetContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkOwnerWitness is a helper function which checks whether the invoker is the
|
|
||||||
// owner of the contract.
|
|
||||||
func checkOwnerWitness() bool {
|
|
||||||
// This is used to verify if a transfer of system assets (NEO and Gas)
|
|
||||||
// involving this contract's address can proceed.
|
|
||||||
if trigger == runtime.Application {
|
|
||||||
// Check if the invoker is the owner of the contract.
|
|
||||||
return runtime.CheckWitness(token.Owner)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decimals returns the token decimals
|
|
||||||
func Decimals() int {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
panic("invalid trigger")
|
|
||||||
}
|
|
||||||
return token.Decimals
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symbol returns the token symbol
|
|
||||||
func Symbol() string {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
panic("invalid trigger")
|
|
||||||
}
|
|
||||||
return token.Symbol
|
|
||||||
}
|
|
||||||
|
|
||||||
// TotalSupply returns the token total supply value
|
|
||||||
func TotalSupply() int {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
panic("invalid trigger")
|
|
||||||
}
|
|
||||||
return getIntFromDB(ctx, token.CirculationKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalanceOf returns the amount of token on the specified address
|
|
||||||
func BalanceOf(holder interop.Hash160) int {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
panic("invalid trigger")
|
|
||||||
}
|
|
||||||
return getIntFromDB(ctx, holder)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer transfers specified amount of token from one user to another
|
|
||||||
func Transfer(from, to interop.Hash160, amount int, _ interface{}) bool {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if amount <= 0 || len(to) != 20 || !runtime.CheckWitness(from) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
amountFrom := getIntFromDB(ctx, from)
|
|
||||||
if amountFrom < amount {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if amountFrom == amount {
|
|
||||||
storage.Delete(ctx, from)
|
|
||||||
} else {
|
|
||||||
diff := amountFrom - amount
|
|
||||||
storage.Put(ctx, from, diff)
|
|
||||||
}
|
|
||||||
amountTo := getIntFromDB(ctx, to)
|
|
||||||
totalAmountTo := amountTo + amount
|
|
||||||
if totalAmountTo != 0 {
|
|
||||||
storage.Put(ctx, to, totalAmountTo)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransferFrom transfers specified amount of token from one user to another.
|
|
||||||
// It differs from Transfer in that it use allowance value to store the amount
|
|
||||||
// of token available to transfer.
|
|
||||||
func TransferFrom(from, to []byte, amount int) bool {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if amount <= 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
availableKey := append(from, to...)
|
|
||||||
if len(availableKey) != 40 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
availableTo := getIntFromDB(ctx, availableKey)
|
|
||||||
if availableTo < amount {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fromBalance := getIntFromDB(ctx, from)
|
|
||||||
if fromBalance < amount {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
toBalance := getIntFromDB(ctx, to)
|
|
||||||
newFromBalance := fromBalance - amount
|
|
||||||
newToBalance := toBalance + amount
|
|
||||||
storage.Put(ctx, to, newToBalance)
|
|
||||||
storage.Put(ctx, from, newFromBalance)
|
|
||||||
|
|
||||||
newAllowance := availableTo - amount
|
|
||||||
if newAllowance == 0 {
|
|
||||||
storage.Delete(ctx, availableKey)
|
|
||||||
} else {
|
|
||||||
storage.Put(ctx, availableKey, newAllowance)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Approve stores token transfer data if the owner has enough token to send.
|
|
||||||
func Approve(owner, spender []byte, amount int) bool {
|
|
||||||
if !checkOwnerWitness() || amount < 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(spender) != 20 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
toSpend := getIntFromDB(ctx, owner)
|
|
||||||
if toSpend < amount {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
approvalKey := append(owner, spender...)
|
|
||||||
if amount == 0 {
|
|
||||||
storage.Delete(ctx, approvalKey)
|
|
||||||
} else {
|
|
||||||
storage.Put(ctx, approvalKey, amount)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allowance returns allowance value for specified sender and receiver.
|
|
||||||
func Allowance(from, to []byte) interface{} {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
key := append(from, to...)
|
|
||||||
return getIntFromDB(ctx, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mint initial supply of tokens
|
|
||||||
func Mint(to []byte) bool {
|
|
||||||
if trigger != runtime.Application {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !checkOwnerWitness() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
minted := storage.Get(ctx, []byte("minted"))
|
|
||||||
if minted != nil && minted.(bool) == true {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.Put(ctx, to, token.TotalSupply)
|
|
||||||
storage.Put(ctx, []byte("minted"), true)
|
|
||||||
addToCirculation(token.TotalSupply)
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
name: "My awesome token"
|
|
||||||
sourceurl: https://github.com/nspcc-dev/neo-go/
|
|
||||||
supportedstandards: ["NEP-17"]
|
|
||||||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"]
|
|
||||||
events:
|
|
||||||
- name: Transfer
|
|
||||||
parameters:
|
|
||||||
- name: from
|
|
||||||
type: Hash160
|
|
||||||
- name: to
|
|
||||||
type: Hash160
|
|
||||||
- name: amount
|
|
||||||
type: Integer
|
|
Loading…
Reference in a new issue