Neo Smart Economy

Python


C#


Go


Typescript


Java


Python

Python


from boa3.builtin.contract import Nep17TransferEvent, abort

@metadata
def manifest_metadata() -> NeoMetadata:
    meta = NeoMetadata()
    meta.author = "coz"
    return meta

OWNER = UInt160(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
TOKEN_TOTAL_SUPPLY = 100_000_000 * 100_000_000  # 10m total supply * 10^8 (decimals)

# Events
on_transfer = Nep17TransferEvent

@public
def transfer(from_address: UInt160, to_address: UInt160, amount: int, data: Any) -> bool:
    assert len(from_address) == 20 and len(to_address) == 20
    assert amount >= 0

    # The function MUST return false if the from account balance does not have enough tokens to spend.
    from_balance = get(from_address).to_int()
    if from_balance < amount:
        return False

    # The function should check whether the from address equals the caller contract hash.
    if from_address != calling_script_hash:
        if not check_witness(from_address):
            return False

    # skip balance changes if transferring to yourself or transferring 0 cryptocurrency
    if from_address != to_address and amount != 0:
        if from_balance == amount:
            delete(from_address)
        else:
            put(from_address, from_balance - amount)

        to_balance = get(to_address).to_int()
        put(to_address, to_balance + amount)

    on_transfer(from_address, to_address, amount)
    # if the to_address is a smart contract, it must call the contract's onPayment method
    post_transfer(from_address, to_address, amount, data)
    return True

Python Resources


Documentation


Templates


Tools

C#

C#


using Neo;
using Neo.SmartContract;
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Native;
using Neo.SmartContract.Framework.Services;
using System;
using System.Numerics;

namespace Desktop
{
    [ManifestExtra("Author", "Neo")]
    [ManifestExtra("Email", "[email protected]")]
    [ManifestExtra("Description", "This is a contract example")]
    [ContractSourceCode("https://github.com/neo-project/neo-devpack-dotnet/tree/master/src/Neo.SmartContract.Template")]
    public class Contract1 : SmartContract
    {
        //TODO: Replace it with your own address.
        [InitialValue("NiNmXL8FjEUEs1nfX9uHFBNaenxDHJtmuB", ContractParameterType.Hash160)]
        static readonly UInt160 Owner = default;

        private static bool IsOwner() => Runtime.CheckWitness(Owner);

        // When this contract address is included in the transaction signature,
        // this method will be triggered as a VerificationTrigger to verify that the signature is correct.
        // For example, this method needs to be called when withdrawing token from the contract.
        public static bool Verify() => IsOwner();

        // TODO: Replace it with your methods.
        public static string MyMethod()
        {
            return Storage.Get(Storage.CurrentContext, "Hello");
        }

        public static void _deploy(object data, bool update)
        {
            if (update) return;

            // It will be executed during deploy
            Storage.Put(Storage.CurrentContext, "Hello", "World");
        }

        public static void Update(ByteString nefFile, string manifest)
        {
            if (!IsOwner()) throw new Exception("No authorization.");
            ContractManagement.Update(nefFile, manifest, null);
        }

        public static void Destroy()
        {
            if (!IsOwner()) throw new Exception("No authorization.");
            ContractManagement.Destroy();
        }
    }
}

CSharp Resources


Documentation


Templates


Tools

Go

Go


package nep17Contract

var (
    token nep17.Token
    ctx   storage.Context
)
// initializes the Token Interface and storage context
func init() {
    token = nep17.Token{
        ...
        Name:           "Nep17 example",
        Owner:          util.FromAddress("NdHjSPVnw99RDMCoJdCnAcjkE23gvqUeg2"),
        TotalSupply:    10000000000000000
    }
    ctx = storage.GetContext()
}
// Transfer token from one user to another
func (t Token) Transfer(ctx storage.Context, from, to interop.Hash160, amount int, data interface{}) 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 := getIntFromDB(ctx, to)
    totalAmountTo := amountTo + amount
    storage.Put(ctx, to, totalAmountTo)
    runtime.Notify("Transfer", from, to, amount)
    if to != nil && management.GetContract(to) != nil {
        contract.Call(to, "onNEP17Payment", contract.All, from, amount, data)
    }
    return true
}

Go Resources


Documentation


Templates


Tools

Typescript

Typescript


import {  SmartContract} from '@neo-one/smart-contract';

export class NEP17Contract extends SmartContract {
  public readonly properties = {
    name: 'NEO•ONE NEP17 Example',
    groups: [],
    trusts: '*',
    permissions: [],
  };
  public readonly name = 'NEO•ONE NEP17 Example';
  public readonly decimals = 8;

  private readonly notifyTransfer = createEventNotifier<Address | undefined, Address | undefined, Fixed<8>>(
    'Transfer', 'from', 'to', 'amount',
  );

  public transfer(from: Address, to: Address, amount: Fixed<8>, data?: any): boolean {
    if (amount < 0) {throw new Error(`Amount must be greater than 0: ${amount}`);}

    const fromBalance = this.balanceOf(from);
    if (fromBalance < amount) { return false; }

    const contract = Contract.for(to);
    if (contract !== undefined && !Address.isCaller(to)) {
      const smartContract = SmartContract.for<TokenPayableContract>(to);
      if (!smartContract.approveReceiveTransfer(from, amount, this.address)) {
        return false;
      }
    }

    const toBalance = this.balanceOf(to);
    this.balances.set(from, fromBalance - amount);
    this.balances.set(to, toBalance + amount);
    this.notifyTransfer(from, to, amount);

    if (contract !== undefined) {
      const smartContract = SmartContract.for<TokenPayableContract>(to);
      smartContract.onNEP17Payable(from, amount, data);
    }
    return true;
  }
}

Typescript Resources


Documentation


Templates


Tools

Java

Java


package io.neow3j.examples.contractdevelopment.contracts;

import static io.neow3j.devpack.StringLiteralHelper.addressToScriptHash;

import io.neow3j.devpack.*

@ManifestExtra(key = "name", value = "FungibleToken")
@ManifestExtra(key = "author", value = "AxLabs")
@SupportedStandards("NEP-17")
@Permission(contract = "fffdc93764dbaddd97c48f252a53ea4643faa3fd") // ContractManagement
public class FungibleToken {

    static final Hash160 owner = addressToScriptHash("NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP");

    @DisplayName("Transfer")
    static Event3Args onTransfer;

    static final int initialSupply = 200_000_000;
    static final int decimals = 8;
    static final String assetPrefix = "asset";
    static final String totalSupplyKey = "totalSupply";
    static final StorageContext sc = Storage.getStorageContext();
    static final StorageMap assetMap = sc.createMap(assetPrefix);

    public static String symbol() {
        return "FGT";
    }

    public static int decimals() {
        return decimals;
    }

    public static int totalSupply() {
        return getTotalSupply();
    }

    static int getTotalSupply() {
        return Storage.getInteger(sc, totalSupplyKey);
    }

    public static boolean transfer(Hash160 from, Hash160 to, int amount, Object[] data)
            throws Exception {

        if (!Hash160.isValid(from) || !Hash160.isValid(to)) {
            throw new Exception("From or To address is not a valid address.");
        }
        if (amount < 0) {
            throw new Exception("The transfer amount was negative.");
        }
        if (!Runtime.checkWitness(from) && from != Runtime.getCallingScriptHash()) {
            throw new Exception("Invalid sender signature. The sender of the tokens needs to be "
                    + "the signing account.");
        }
        if (getBalance(from) < amount) {
            return false;
        }
        if (from != to && amount != 0) {
            deductFromBalance(from, amount);
            addToBalance(to, amount);
        }

        onTransfer.fire(from, to, amount);
        if (ContractManagement.getContract(to) != null) {
            Contract.call(to, "onNEP17Payment", CallFlags.All, data);
        }

        return true;
    }

    public static int balanceOf(Hash160 account) throws Exception {
        if (!Hash160.isValid(account)) {
            throw new Exception("Argument is not a valid address.");
        }
        return getBalance(account);
    }

    @OnDeployment
    public static void deploy(Object data, boolean update) throws Exception {
        throwIfSignerIsNotOwner();
        if (!update) {
            if (Storage.get(sc, totalSupplyKey) != null) {
                throw new Exception("Contract was already deployed.");
            }
            // Initialize supply
            Storage.put(sc, totalSupplyKey, initialSupply);
            // And allocate all tokens to the contract owner.
            assetMap.put(owner.toByteArray(), initialSupply);
        }
    }

    public static void update(ByteString script, String manifest) throws Exception {
        throwIfSignerIsNotOwner();
        if (script.length() == 0 && manifest.length() == 0) {
            throw new Exception("The new contract script and manifest must not be empty.");
        }
        ContractManagement.update(script, manifest);
    }

    public static void destroy() throws Exception {
        throwIfSignerIsNotOwner();
        ContractManagement.destroy();
    }

    @OnVerification
    public static boolean verify() throws Exception {
        throwIfSignerIsNotOwner();
        return true;
    }

    /**
     * Gets the address of the contract owner.
     *
     * @return the address of the contract owner.
     */
    public static Hash160 contractOwner() {
        return owner;
    }

    private static void throwIfSignerIsNotOwner() throws Exception {
        if (!Runtime.checkWitness(owner)) {
            throw new Exception("The calling entity is not the owner of this contract.");
        }
    }

    private static void addToBalance(Hash160 key, int value) {
        assetMap.put(key.toByteArray(), getBalance(key) + value);
    }

    private static void deductFromBalance(Hash160 key, int value) {
        int oldValue = getBalance(key);
        if (oldValue == value) {
            assetMap.delete(key.toByteArray());
        } else {
            assetMap.put(key.toByteArray(), oldValue - value);
        }
    }

    private static int getBalance(Hash160 key) {
        return assetMap.getInteger(key.toByteArray());
    }

}
    

Java Resources


Documentation


Templates


Tools

Learn More