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
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", "dev@neo.org")] [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(); } } }
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 }
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; } }
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()); } }
Learn More