NEO常见的资产有三种
- TOKEN (全局资产)
- Share (全局资产,股份 )
- NEP-5 (合约代币,相当于ETH的ERC20)
NEP-5 合约代码
https://github.com/ANDIT-Solutions/NEO-NEP5.1
这里发行Name为Five的代币,代币Symbol为FFF. 共发行1000000000个单位。
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using System;
using System.ComponentModel;
using System.Numerics;
namespace Neo.SmartContract
{
public class ICO_Template : Framework.SmartContract
{
//Token Settings
public static string Name() => "Five";
public static string Symbol() => "FFF";
public static readonly byte[] Owner = "AdNjgXiDU1imfKoCSQcgcd4HmBSn2qsHfe".ToScriptHash();//translates
//to: 0x20f0a44d41c659a49651823ac19794dda45bd4d9., it's the ScriptHash of your public address ,you can view it in neo-gui(for example)
public static byte Decimals() => 8;
private const ulong factor = 100000000; //decided by Decimals()
//Since we don't want to deal with floating points sending 555 tokens with 8 Decimals (100000000)
//would actually mean that you want to send 55500000000 tokens.
private const ulong total_amount = 1000000000 * factor; //token amount
[DisplayName("transfer")]
public static event Action<byte[], byte[], BigInteger> Transferred;
public static Object Main(string operation, params object[] args)
{
if (Runtime.Trigger == TriggerType.Verification)
{
if (Owner.Length == 20)
{
// if param Owner is script hash
return Runtime.CheckWitness(Owner);
}
else if (Owner.Length == 33)
{
// if param Owner is public key
byte[] signature = operation.AsByteArray();
return VerifySignature(signature, Owner);
}
}
else if (Runtime.Trigger == TriggerType.Application)
{
if (operation == "deploy") return Deploy();
if (operation == "totalSupply") return TotalSupply();
if (operation == "name") return Name();
if (operation == "symbol") return Symbol();
if (operation == "decimals") return Decimals();
if (operation == "approve") if (args.Length >= 3) return Approve((byte[])args[0], (byte[])args[1], (BigInteger)args[2]); else return NotifyErrorAndReturn0("argument count must be atleast 3");
if (operation == "allowance") if (args.Length >= 2) return Allowance((byte[])args[0], (byte[])args[1]); else return NotifyErrorAndReturn0("argument count must be atleast 2");
if (operation == "transferFrom") if (args.Length >= 4) return TransferFrom((byte[])args[0], (byte[])args[1], (byte[])args[2], (BigInteger)args[3]); else return NotifyErrorAndReturn0("argument count must be atleast 4");
if (operation == "transfer")
{
if (args.Length != 3 || args[0] == null || ((byte[])args[0]).Length == 0 || args[1] == null || ((byte[])args[1]).Length == 0) return NotifyErrorAndReturnFalse("argument count must be 3 and they must not be null");
byte[] from = (byte[])args[0];
byte[] to = (byte[])args[1];
BigInteger value = (BigInteger)args[2];
return Transfer(from, to, value, false);
}
if (operation == "balanceOf")
{
if (args.Length != 1 || args[0] == null || ((byte[])args[0]).Length == 0) return NotifyErrorAndReturn0("argument count must be 1 and they must not be null");
byte[] account = (byte[])args[0];
return BalanceOf(account);
}
}
return NotifyErrorAndReturnFalse("Operation not found");
}
/// <summary>
/// Can only be called by the Owner
/// Puts total_amount value under "totalSupply" key and ,this function can be used only once ,unless the total amount is 0.
/// </summary>
/// <returns></returns>
public static bool Deploy()
{
if (!Runtime.CheckWitness(Owner)) //ensure that it is the owner calling this method
return NotifyErrorAndReturnFalse("You are not the Owner of this Smart Contract");
byte[] total_supply = Storage.Get(Storage.CurrentContext, "totalSupply");
if (total_supply.Length != 0)
return NotifyErrorAndReturnFalse("Looks like this method has been allready used");
Storage.Put(Storage.CurrentContext, Owner, total_amount);
Storage.Put(Storage.CurrentContext, "totalSupply", total_amount);
Transferred(null, Owner, total_amount);
return true;
}
/// <summary>
/// Returns the Total Supply of Tokens
/// </summary>
/// <returns></returns>
public static BigInteger TotalSupply()
{
return Storage.Get(Storage.CurrentContext, "totalSupply").AsBigInteger();
}
/// <summary>
/// Checks the TransferFrom approval of two accounts.
/// </summary>
/// <param name="from">
/// The account which funds can be transfered from.
/// </param>
/// <param name="to">
/// The account which is granted usage of the account.
/// </param>
/// <returns>
/// The amount allocated for TransferFrom.
/// </returns>
public static BigInteger Allowance(byte[] from, byte[] to)
{
if (from == null || from.Length != 20)
return NotifyErrorAndReturn0("From value must not be empty and have size of 20");
if (to == null || to.Length != 20)
return NotifyErrorAndReturn0("To value must not be empty and have size of 20");
return Storage.Get(Storage.CurrentContext, from.Concat(to)).AsBigInteger();
}
/// <summary>
/// Approves another user to use the TransferFrom
/// function on the invoker's account.
/// TransferFrom
/// </summary>
/// <param name="originator">
/// The contract invoker.
/// </param>
/// <param name="to">
/// The account to grant TransferFrom access to.
/// 所批准的账户
/// </param>
/// <param name="amount">
/// The amount to grant TransferFrom access for.
/// </param>
/// <returns>
/// Transaction Successful?
/// </returns>
public static bool Approve(byte[] originator, byte[] to, BigInteger amount)
{
if (to == null || to.Length != 20)
return NotifyErrorAndReturnFalse("To value must not be empty and have size of 20");
if (originator == null || originator.Length != 20)
return NotifyErrorAndReturnFalse("Originator value must not be empty and have size of 20");
if (amount < 0)
return NotifyErrorAndReturnFalse("Amount is lower than zero");
if (!(Runtime.CheckWitness(originator)))
return NotifyErrorAndReturnFalse("Originator isn't associated with this invoke");
Storage.Put(Storage.CurrentContext, originator.Concat(to), amount);
return true;
}
/// <summary>
/// Transfers a balance from one account to another
/// on behalf of the account owner.
/// </summary>
/// <param name="originator">
/// The contract invoker.
/// </param>
/// <param name="from">
/// The account to transfer a balance from.
/// </param>
/// <param name="to">
/// The account to transfer a balance to.
/// </param>
/// <param name="amount">
/// The amount to transfer.
/// </param>
/// <returns>
/// Transaction successful?
/// </returns>
public static bool TransferFrom(byte[] originator, byte[] from, byte[] to, BigInteger amountToSend)
{
if (originator == null || originator.Length != 20)
return NotifyErrorAndReturnFalse("Originator value must not be empty and have size of 20");
if (from == null || from.Length != 20)
return NotifyErrorAndReturnFalse("From value must not be empty and have size of 20");
if (to == null || to.Length != 20)
return NotifyErrorAndReturnFalse("To value must not be empty and have size of 20");
if (!(Runtime.CheckWitness(originator)))
return NotifyErrorAndReturnFalse("Originator isn't associated with this invoke");
if (amountToSend <= 0)
return NotifyErrorAndReturnFalse("You need to send more than 0 tokens");
BigInteger ownerAmount = BalanceOf(from);
if (ownerAmount < amountToSend)
return NotifyErrorAndReturnFalse("Owner doesn't have this kind amount of tokens");
if (ownerAmount <= 0)
return NotifyErrorAndReturnFalse("Owner doesn't have positive balance");
BigInteger allowedAmount = Allowance(from, originator);
if (allowedAmount < amountToSend)
return NotifyErrorAndReturnFalse("You are trying to send more than you are allowed to");
if (!(Transfer(from, to, amountToSend, true)))//This does the actual transfer.
return NotifyErrorAndReturnFalse("Failed to Transfer");
BigInteger amountLeft = allowedAmount - amountToSend;
if (amountLeft <= 0)//There should not be an situation where someone holds an negative ballance ,but for safety sake I did add this.
{
Storage.Delete(Storage.CurrentContext, from.Concat(originator));
}
else
{
Storage.Put(Storage.CurrentContext, from.Concat(originator), amountLeft);
}
return true;
}
/// <summary>
/// function that is always called when someone wants to transfer tokens.
/// </summary>
/// <param name="from"></param>
/// wallet from which we send the tokens.
/// <param name="to"></param>
/// wallet to which we send the tokens.
/// <param name="value"></param>
/// amount that we will send
/// <param name="transferFrom"></param>
/// Checks if the function is called from TransferFrom methods ,if so then there is no need to Check Witness.
/// <returns>
/// Returns if the tranfer process has succeded or not.
/// </returns>
public static bool Transfer(byte[] from, byte[] to, BigInteger value, bool transferFrom)
{
if (to == null || to.Length != 20)
return NotifyErrorAndReturnFalse("To value must not be empty and have size of 20");
if (from == null || from.Length != 20)
return NotifyErrorAndReturnFalse("From value must not be empty and have size of 20");
if (value <= 0) return NotifyErrorAndReturnFalse("Try to send more than 0 tokens");
if (!transferFrom && !Runtime.CheckWitness(from)) return NotifyErrorAndReturnFalse("Owner of the wallet is not involved in this invoke");
if (from == to) return true;
BigInteger from_value = Storage.Get(Storage.CurrentContext, from).AsBigInteger();
if (from_value < value) return NotifyErrorAndReturnFalse("Insufficient funds");
if (from_value == value)
Storage.Delete(Storage.CurrentContext, from);
else
Storage.Put(Storage.CurrentContext, from, from_value - value);
BigInteger to_value = Storage.Get(Storage.CurrentContext, to).AsBigInteger();
Storage.Put(Storage.CurrentContext, to, to_value + value);
Transferred(from, to, value);
return true;
}
/// <summary>
/// Get the account balance of another account with address,uses wallets address scripthash.
/// </summary>
/// <param name="address"></param>
/// The Adress that we want to know blance of
/// <returns>
/// Amount of tokens associated with this address
/// </returns>
public static BigInteger BalanceOf(byte[] address)
{
return Storage.Get(Storage.CurrentContext, address).AsBigInteger();
}
/// <summary>
/// Used when we want to Notify Error for debbuging and return false
/// </summary>
/// <param name="value"></param>
/// String that will be notified
/// <returns>
/// Allways False
/// </returns>
public static bool NotifyErrorAndReturnFalse(string value)
{
Runtime.Notify(value);
return false;
}
/// <summary>
/// Used when we want to Notify Error for debbuging and return 0
/// </summary>
/// <param name="value"></param>
/// String that will be notified
/// <returns>
/// Allways 0
/// </returns>
public static int NotifyErrorAndReturn0(string value)
{
Runtime.Notify(value);
return 0;
}
}
}
需要定义的参数:
参数说明:
- Owner 合约部署后,代币所有者
- factor 小数位数,Decimal
- total_amount 总发行量
- Name 合约名称
- Sympol 代币名称
编译部署
把C#代码放到https://neocompiler.io/#/ecolab,线上编译即可
下载AVM的16进制格式编码和Script hash:
hex code:
54c56b6c766b00527ac46c766b51527ac46168164e656f2e52756e74696d652e47657454726967676572639a006114ecf28d2236dab6c09da8352821780215e890d7cfc00114907c907c9e6339006114ecf28d2236dab6c09da8352821780215e890d7cf6168184e656f2e52756e74696d652e436865636b5769746e657373616c75666114ecf28d2236dab6c09da8352821780215e890d7cfc00121907c907c9e6357036c766b00c36114ecf28d2236dab6c09da8352821780215e890d7cfac616c75666168164e656f2e52756e74696d652e4765745472696767657260907c907c9e6315036c766b00c3066465706c6f7987640b0061653c03616c75666c766b00c30b746f74616c537570706c7987640b006165d304616c75666c766b00c3046e616d6587640b006165ea02616c75666c766b00c30673796d626f6c87640b006165de02616c75666c766b00c308646563696d616c7387640b006165cf02616c75666c766b00c307617070726f7665876456006c766b51c3c0539f6322006c766b51c300c36c766b51c351c36c766b51c352c3615272657305616c756620617267756d656e7420636f756e74206d7573742062652061746c65617374203361658a0d616c75666c766b00c309616c6c6f77616e636587644e006c766b51c3c0529f631a006c766b51c300c36c766b51c351c3617c653104616c756620617267756d656e7420636f756e74206d7573742062652061746c65617374203261652c0d616c75666c766b00c30c7472616e7366657246726f6d87646f006c766b51c3c0549f633b006c766b51c300c36c766b51c351c36c766b51c352c36c766b51c353c3615379517955727551727552795279547275527275650f06616c756620617267756d656e7420636f756e74206d7573742062652061746c6561737420346165aa0c616c75666c766b00c3087472616e736665728764c1006c766b51c3c053907c907c9e632d006c766b51c300c36423006c766b51c300c3c06418006c766b51c351c3640e006c766b51c351c3c0633e0032617267756d656e7420636f756e74206d757374206265203320616e642074686579206d757374206e6f74206265206e756c6c6165f60b616c75666c766b51c300c36c766b51c351c36c766b52527ac46c766b51c352c36c766b53527ac46c766b52c36c766b53c300615379517955727551727552795279547275527275657908616c75666c766b00c30962616c616e63654f66876471006c766b51c3c051907c907c9e6318006c766b51c300c3640e006c766b51c300c3c0633e0032617267756d656e7420636f756e74206d757374206265203120616e642074686579206d757374206e6f74206265206e756c6c6165680b616c75666c766b51c300c36165ec0a616c7566134f7065726174696f6e206e6f7420666f756e6461650f0b616c756600c56b0446697665616c756600c56b03464646616c756600c56b58616c756600c56b6114ecf28d2236dab6c09da8352821780215e890d7cf6168184e656f2e52756e74696d652e436865636b5769746e6573736338002c596f7520617265206e6f7420746865204f776e6572206f66207468697320536d61727420436f6e74726163746165840a616c75666168164e656f2e53746f726167652e476574436f6e746578740b746f74616c537570706c79617c680f4e656f2e53746f726167652e476574c06439002d4c6f6f6b73206c696b652074686973206d6574686f6420686173206265656e20616c6c726561647920757365646165120a616c75666168164e656f2e53746f726167652e476574436f6e746578746114ecf28d2236dab6c09da8352821780215e890d7cf0800008a5d78456301615272680f4e656f2e53746f726167652e5075746168164e656f2e53746f726167652e476574436f6e746578740b746f74616c537570706c790800008a5d78456301615272680f4e656f2e53746f726167652e50757461006114ecf28d2236dab6c09da8352821780215e890d7cf0800008a5d78456301615272087472616e7366657254c168124e656f2e52756e74696d652e4e6f7469667951616c756600c56b6168164e656f2e53746f726167652e476574436f6e746578740b746f74616c537570706c79617c680f4e656f2e53746f726167652e476574616c756652c56b6c766b00527ac46c766b51527ac46c766b00c3640f006c766b00c3c001149c633c003046726f6d2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f662032306165cd08616c75666c766b51c3640f006c766b51c3c001149c633a002e546f2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f6620323061658208616c75666168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b51c37e617c680f4e656f2e53746f726167652e476574616c756653c56b6c766b00527ac46c766b51527ac46c766b52527ac46c766b51c3640f006c766b51c3c001149c633a002e546f2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f662032306165b607616c75666c766b00c3640f006c766b00c3c001149c634200364f726967696e61746f722076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f6620323061656307616c75666c766b52c3009f64250019416d6f756e74206973206c6f776572207468616e207a65726f61653707616c75666c766b00c36168184e656f2e52756e74696d652e436865636b5769746e6573736338002c4f726967696e61746f722069736e2774206173736f6369617465642077697468207468697320696e766f6b656165df06616c75666168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b51c37e6c766b52c3615272680f4e656f2e53746f726167652e50757451616c756657c56b6c766b00527ac46c766b51527ac46c766b52527ac46c766b53527ac46c766b00c3640f006c766b00c3c001149c634200364f726967696e61746f722076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f6620323061652b06616c75666c766b51c3640f006c766b51c3c001149c633c003046726f6d2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f662032306165de05616c75666c766b52c3640f006c766b52c3c001149c633a002e546f2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f6620323061659305616c75666c766b00c36168184e656f2e52756e74696d652e436865636b5769746e6573736338002c4f726967696e61746f722069736e2774206173736f6369617465642077697468207468697320696e766f6b6561653b05616c75666c766b53c300a1642f0023596f75206e65656420746f2073656e64206d6f7265207468616e203020746f6b656e7361650505616c75666c766b51c36165b9046c766b54527ac46c766b54c36c766b53c39f6439002d4f776e657220646f65736e277420686176652074686973206b696e6420616d6f756e74206f6620746f6b656e736165b104616c75666c766b54c300a1642f00234f776e657220646f65736e2774206861766520706f7369746976652062616c616e636561657b04616c75666c766b51c36c766b00c3617c6572fb6c766b55527ac46c766b55c36c766b53c39f643f0033596f752061726520747279696e6720746f2073656e64206d6f7265207468616e20796f752061726520616c6c6f77656420746f61651b04616c75666c766b51c36c766b52c36c766b53c35161537951795572755172755279527954727552727565bc00631e00124661696c656420746f205472616e736665726165d503616c75666c766b55c36c766b53c3946c766b56527ac46c766b56c300a16440006168164e656f2e53746f726167652e476574436f6e746578746c766b51c36c766b00c37e617c68124e656f2e53746f726167652e44656c6574656240006168164e656f2e53746f726167652e476574436f6e746578746c766b51c36c766b00c37e6c766b56c3615272680f4e656f2e53746f726167652e50757451616c756656c56b6c766b00527ac46c766b51527ac46c766b52527ac46c766b53527ac46c766b51c3640f006c766b51c3c001149c633a002e546f2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f662032306165d002616c75666c766b00c3640f006c766b00c3c001149c633c003046726f6d2076616c7565206d757374206e6f7420626520656d70747920616e6420686176652073697a65206f6620323061658302616c75666c766b52c300a1642a001e54727920746f2073656e64206d6f7265207468616e203020746f6b656e7361655202616c75666c766b53c36361006c766b00c36168184e656f2e52756e74696d652e436865636b5769746e657373633e00324f776e6572206f66207468652077616c6c6574206973206e6f7420696e766f6c76656420696e207468697320696e766f6b656165ec01616c75666c766b00c36c766b51c3907c907c9e63080051616c75666168164e656f2e53746f726167652e476574436f6e746578746c766b00c3617c680f4e656f2e53746f726167652e4765746c766b54527ac46c766b54c36c766b52c39f641e0012496e73756666696369656e742066756e647361657401616c75666c766b54c36c766b52c39c643a006168164e656f2e53746f726167652e476574436f6e746578746c766b00c3617c68124e656f2e53746f726167652e44656c6574656240006168164e656f2e53746f726167652e476574436f6e746578746c766b00c36c766b54c36c766b52c394615272680f4e656f2e53746f726167652e5075746168164e656f2e53746f726167652e476574436f6e746578746c766b51c3617c680f4e656f2e53746f726167652e4765746c766b55527ac46168164e656f2e53746f726167652e476574436f6e746578746c766b51c36c766b55c36c766b52c393615272680f4e656f2e53746f726167652e507574616c766b00c36c766b51c36c766b52c3615272087472616e7366657254c168124e656f2e52756e74696d652e4e6f7469667951616c756651c56b6c766b00527ac46168164e656f2e53746f726167652e476574436f6e746578746c766b00c3617c680f4e656f2e53746f726167652e476574616c756651c56b6c766b00527ac451c576006c766b00c3c46168124e656f2e52756e74696d652e4e6f7469667900616c756651c56b6c766b00527ac451c576006c766b00c3c46168124e656f2e52756e74696d652e4e6f7469667900616c7566
####### Script Hash
b2e50c6a25fdc5c690992c36d9b9f2978b7062fd
部署合约
高级-部署合约
部署NEP-5合约花费490GAS 交易类型为InvocationTransaction
高级-合约调用-调用合约内deploy函数
NEP-5 转账
先在neo-gui添加代币资产
Advanced
Options
Add Five Scripthash: b2e50c6a25fdc5c690992c36d9b9f2978b7062fd
Apply/Ok
可以看到资产中有刚才发行的Five代币
NEP-5 代币转账
交易所对接NEP-5
- 节点开启log日志
dotnet neo-cli.dll --rpc --log --nopeers
- 解析log中NEP-5交易
root@test:/data/app/neo-cli/ApplicationLogs_74746E41# cat 0x5a59e800a82ca8baabd4dad7fcd8b6690eb475c539651f0e3d54034f419efdf3.json
{"txid":"0x5a59e800a82ca8baabd4dad7fcd8b6690eb475c539651f0e3d54034f419efdf3","vmstate":"HALT, BREAK","gas_consumed":"2.739","stack":[],"notifications":[{"contract":"0xb2e50c6a25fdc5c690992c36d9b9f2978b7062fd","state":{"type":"Array","value":[{"type":"ByteArray","value":"7472616e73666572"},{"type":"ByteArray","value":"ecf28d2236dab6c09da8352821780215e890d7cf"},{"type":"ByteArray","value":"586853337b382a817c163b965eb57c1b57046519"},{"type":"ByteArray","value":"00e8764817"}]}}]}
- 调用NEP-5合约balaceOf() 函数确认余额