- Smart Contracts
- Changes in NEO3
- Manifest
- Trigger
- Native Contract
- Interop Service
- Principle of Interop Service
- Usage of Interop Service
- System Part
- System.Binary.Serialize
- System.Binary.Deserialize
- System.Blockchain.GetHeight
- System.Blockchain.GetBlock
- System.Blockchain.GetTransaction
- System.Blockchain.GetTransactionHeight
- System.Blockchain.GetTransactionFromBlock
- System.Blockchain.GetContract
- System.Contract.Create
- System.Contract.Update
- System.Contract.Destroy
- System.Contract.Call
- System.Contract.CallEx
- System.Contract.IsStandard
- System.Enumerator.Create
- System.Enumerator.Next
- System.Enumerator.Value
- System.Enumerator.Concat
- System.Iterator.Create
- System.Iterator.Key
- System.Iterator.Keys
- System.Iterator.Values
- System.Iterator.Concat
- System.Json.Serialize
- System.Json.Deserialize
- System.Runtime.Platform
- System.Runtime.GetTrigger
- System.Runtime.GetTime
- System.Runtime.GetScriptContainer
- System.Runtime.GetExecutingScriptHash
- System.Runtime.GetCallingScriptHash
- System.Runtime.GetEntryScriptHash
- System.Runtime.CheckWitness
- System.Runtime.GetInvocationCounter
- System.Runtime.Log
- System.Runtime.Notify
- System.Runtime.GetNotifications
- System.Runtime.GasLeft
- System.Storage.GetContext
- System.Storage.GetReadOnlyContext
- System.Storage.Get
- System.Storage.Find
- System.Storage.Put
- System.Storage.PutEx
- System.Storage.Delete
- System.StorageContext.AsReadOnly
- Neo Part
- Accessing to Internet Resources
- Contract Invocation
- Contract Upgrade
- Contract Destroying
All transactions in NEO3 are the invocation of the smart contract. In addition to some interop services and OpCode adjustments, NEO3 also features the following changes.
-
ADD
- Manifest: used to describe the features of the contract and deployed with NEF files
- native contracts: running in the native code rather than in the virtual machine, including NeoToken, GasToken and PolicyToken
- Accessing to network resources: to be added
- System Trigger: triggered when the node receives a new block and currently only triggers the execution of the native contract
- Interop Service:
System.Binary.Serialize
,System.Binary.Deserialize
,System.Contract.Create
,System.Contract.Update
,System.Contract.Call
,System.Contract.CallEx
,System.Contract.IsStandard
,System.Enumerator.Create
,System.Enumerator.Next
,System.Enumerator.Value
,System.Enumerator.Concat
,System.Iterator.Create
,System.Iterator.Key
,System.Iterator.Keys
,System.Iterator.Values
,System.Iterator.Concat
,System.Json.Serialize
,System.Json.Deserialize
,System.Runtime.GetScriptContainer
,System.Runtime.GetScriptContainer
,System.Runtime.GetExecutingScriptHash
,System.Runtime.GetCallingScriptHash
,System.Runtime.GetEntryScriptHash
,System.Runtime.GetInvocationCounter
,System.Runtime.GetNotifications
,System.Runtime.GasLeft
,System.Storage.Find
,Neo.Native.Deploy
,Neo.Native.Tokens.NEO
,Neo.Native.Tokens.GAS
,Neo.Native.Policy
,Neo.Crypto.ECDsaVerify
,Neo.Crypto.ECDsaCheckMultiSig
.
-
UPDATE
- Reduce the system fee for OpCode and interop services
-
DELETE
- Interop Service:
System.ExecutionEngine.GetScriptContainer
,System.ExecutionEngine.GetExecutingScriptHash
,System.ExecutionEngine.GetCallingScriptHash
,System.ExecutionEngine.GetEntryScriptHash
,System.Runtime.Serialize
,System.Runtime.Deserialize
,System.Header.GetIndex
,System.Header.GetHash
,System.Header.GetPrevHash
,System.Header.GetTimestamp
,System.Block.GetTransactionCount
,System.Block.GetTransactions
,System.Block.GetTransaction
,System.Transaction.GetHash
,System.Contract.GetStorageContext
,Neo.Runtime.GetTrigger
,Neo.Runtime.CheckWitness
,Neo.Runtime.Notify
,Neo.Runtime.Log
,Neo.Runtime.GetTime
,Neo.Runtime.Serialize
,Neo.Runtime.Deserialize
,Neo.Blockchain.GetHeight
,Neo.Blockchain.GetHeader
,Neo.Blockchain.GetBlock
,Neo.Blockchain.GetTransaction
,Neo.Blockchain.GetTransactionHeight
,Neo.Blockchain.GetAccount
,Neo.Blockchain.GetValidators
,Neo.Blockchain.GetAsset
,Neo.Blockchain.GetContract
,Neo.Header.GetHash
,Neo.Header.GetVersion
,Neo.Header.GetPrevHash
,Neo.Header.GetMerkleRoot
,Neo.Header.GetTimestamp
,Neo.Header.GetIndex
,Neo.Header.GetConsensusData
,Neo.Header.GetNextConsensus
,Neo.Block.GetTransactionCount
,Neo.Block.GetTransactions
,Neo.Block.GetTransaction
,Neo.Transaction.GetHash
,Neo.Transaction.GetType
,Neo.Transaction.GetAttributes
,Neo.Transaction.GetInputs
,Neo.Transaction.GetOutputs
,Neo.Transaction.GetReferences
,Neo.Transaction.GetUnspentCoins
,Neo.Transaction.GetWitnesses
,Neo.InvocationTransaction.GetScript
,Neo.Witness.GetVerificationScript
,Neo.Attribute.GetUsage
,Neo.Attribute.GetData
,Neo.Input.GetHash
,Neo.Input.GetIndex
,Neo.Output.GetAssetId
,Neo.Output.GetValue
,Neo.Output.GetScriptHash
,Neo.Account.GetScriptHash
,Neo.Account.GetVotes
,Neo.Account.GetBalance
,Neo.Account.IsStandard
,Neo.Asset.Create
,Neo.Asset.Renew
,Neo.Asset.GetAssetId
,Neo.Asset.GetAssetType
,Neo.Asset.GetAmount
,Neo.Asset.GetAvailable
,Neo.Asset.GetPrecision
,Neo.Asset.GetOwner
,Neo.Asset.GetAdmin
,Neo.Asset.GetIssuer
,Neo.Contract.Create
,Neo.Contract.Migrate
,Neo.Contract.Destroy
,Neo.Contract.GetScript
,Neo.Contract.IsPayable
,Neo.Contract.GetStorageContext
,Neo.Storage.GetContext
,Neo.Storage.GetReadOnlyContext
,Neo.Storage.Get
,Neo.Storage.Put
,Neo.Storage.Delete
,Neo.Storage.Find
,Neo.StorageContext.AsReadOnly
,Neo.Enumerator.Create
,Neo.Enumerator.Next
,Neo.Enumerator.Value
,Neo.Enumerator.Concat
,Neo.Iterator.Create
,Neo.Iterator.Key
,Neo.Iterator.Keys
,Neo.Iterator.Values
,Neo.Iterator.Concat
.
- Interop Service:
Now each contract is required to provide a manifest file to describe its properties, including Groups, Features, ABI, Permissions, Trusts, SafeMethods, as shown below:
{
"groups": [
{
"pubKey": "",
"signature": ""
}
],
"features": {
"storage": false,
"payable": false
},
"abi": {
"hash": "0x562851057d8afbc08fabc8c438d7cc771aef2195",
"entrypoint": {
"name": "main",
"parameters": [
{
"name": "operation",
"type": "String"
},
{
"name": "args",
"type": "Array"
}
],
"returntype": "Any"
},
"methods": [
{
"name": "name",
"parameters": [],
"returntype": "string"
}
],
"events": [
{
"name": "transfered",
"parameters": [
{"name": "from","type": "Hash160"},
{"name": "to","type": "Hash160"},
{"name": "value","type": "Integer"}
]
}
]
},
"permissions": [
{
"contract": "*",
"methods": "*"
}
],
"trusts": "*",
"safemethods": "*"
}
-
Groups:Declare the groups to which the contract belongs. Each group is identified by a public key and a signature.
-
Features:Declare the features of the smart contract. Where the attribute value
storage
indicates that the contract can access the storage area, and thepayable
indicates the contract can accept the transfer of assets. -
ABI:Declare the interface information about the smart contract, you can refer to NEP-3 for the details. The basic properties of the interface include:
- Hash: the script hash of the contract. It is encoded as a hexadecimal string in big-endian;
- EntryPoint: a Method object which describes the details of the entry point of the contract, including name, parameters, and the value returned;
- Methods: an array of Method objects which describe the details of each method in the contract;
- Events: an array of Event objects which describe the details of each event in the contract.
Based on the ABI information, mutual invocation between contracts can be realized.
-
Permissions:Declare which contracts may be invoked and which methods are called. If a contract invokes a contract or method that is not declared in the manifest at runtime, the invocation will fail.
-
Trusts:Declare which contracts or groups can call the contract safely.
-
SafeMethods:Declare an array containing a set of method names. SafeMethods usually won't modify the storage area, only involved in reading the blockchain data. If a method is marked as safe, the user interface will not give any warnings when it is called by any other contract.
Triggers allow contracts to execute different logic depending on the usage scenario.
-
System Trigger
This type of trigger is newly added in NEO3, which is only used for native contract, such as NEO and GAS. It is triggered when the node receives a new block and currently only triggers the execution of the native contract. After receiving a new block, the node will be triggered by the System Trigger to invoke the onPersist method in all native contracts before the persistence.
This system trigger will not bring any affect to the normal smart contract.
-
Application Trigger
An application trigger is used to invoke a contract as an application function, which can accept multiple parameters, change blockchain status, and return values of any type. Here is a simple c# smart contract:
public static Object Main(string operation, params object[] args) { if (Runtime.Trigger == TriggerType.Application) { if (operation == "FunctionA") return FunctionA(args); } } public static bool FunctionA(params object[] args) { //some code }
All transactions in NEO3 are the invocation of the smart contract. When a transaction is broadcast and confirmed, the smart contract is executed by the consensus node, and the normal node does not execute the smart contract when forwarding the transaction. Successful execution of a smart contract does not represent the success of the transaction, and the success of the transaction does not determine the success of the smart contract execution.
-
Verification Trigger
The purpose of the validation trigger is to call the contract as a verification function that accepts multiple arguments and should return a Boolean value indicating the validity of the transaction or block.
When you transfer tokens from account A to account B, the verification contract will be triggered. All the nodes that receive the transaction (including the normal node and the consensus node) will verify the contract of the account A. If the return value is true, the transfer is successful, and fails otherwise.
If the authentication contract fails to execute, the transaction will not be included in the blockchain.
The following code is a simple example of a verification contract. It returns true if condition A is satisfied, indicating the success of the transfer. Otherwise, it returns false and the transfer fails.
using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Neo; public static bool Main(byte[] signature) { if (/*条件A*/) return true; else return false; }
The following code works the same as above, but with an additional check for the trigger in the runtime. The code of the verification part is executed only when the trigger is a verification trigger, which is of great use in complex smart contracts. If a smart contract implements multiple triggers, the type of triggers should be judged in the
Main
method.using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.Neo; public static bool Main(byte[] signature) { if (Runtime.Trigger == TriggerType.Verification) { if (/* condition A*/) return true; else return false; } }
Native contracts are executed directly in native code, rather than in a virtual machine. The native contract exposes its service name to other contracts for invocation. Currently, available NativeContract includes NeoToken, GasToken, and PolicyToken.
Referred to as NEO, it acts as the governance token which is used to enforce the management of the Neo network and conforms to the NEP-5 standard. It has a hard cap total of 100 million tokens, with the smallest unit of 1, and is not divisible. NEO was pre-mined during the genesis block creation. The specific interface details are as follows:
Method marked * is NEP-5 standard interface.
-
name: Name of the token
Example in C# contractParameters none Return Type Description String Name of the token Fee(GAS) 0.00 using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.System; public static object Main(string method, object[] args) { private static string neoScriptHash = "0x43cf98eddbe047e198a3e5d57006311442a0ca15"; if (Runtime.Trigger == TriggerType.Application) { if (method == "neoName") { string name = Contract.Call(neoScriptHash.HexToBytes(), "name", new object[]{}); return name; } } }
Build Script
Using System.Contract.Call to invoke contract:
PUSH0 NEWARRAY PUSHBYTES4 6e616d65 PUSHBYTES20 0x43cf98eddbe047e198a3e5d57006311442a0ca15 SYSCALL 0x627d5b52
C# code to build the script:
ScriptBuilder sb = new ScriptBuilder() UInt160 scriptHash = UInt160.Parse("0x43cf98eddbe047e198a3e5d57006311442a0ca15"); sb.EmitPush(0); sb.Emit(OpCode.NEWARRAY); sb.EmitPush("name"); sb.EmitPush(scriptHash.ToArray()); sb.EmitSysCall(InteropService.System_Contract_Call); byte[] script = sb.ToArray();
-
symbol:Symbol of the token
Parameters none Return Type Description String Symbol of the token Fee(GAS) 0.00 -
decimals: Decimals of the token
Parameters none Return Type Description uint Decimal Fee(GAS) 0.00 -
totalSupply*: Total supply
Parameters none Return Type Description BigInteger Total supply Fee(GAS) 0.01 -
balanceOf: Balance of an account
Parameters Parameter Type Description account Hash160 ScriptHash of the account Return Type Description Integer Balance Fee(GAS) 0.01 -
transfer: Transfer token from one to another
Example in C# contractParameters Parameter Type Description from Hash160 ScriptHash who want to transfer to Hash160 ScriptHash of the receiver amount Integer Amount of the token Return Type Description Boolean Result, true: success,false: failure Fee(GAS) 0.08 using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.System; public static object Main(string method, object[] args) { private static string neoScriptHash = "0x43cf98eddbe047e198a3e5d57006311442a0ca15"; if (Runtime.Trigger == TriggerType.Application) { if (method == "transferNeo") { byte[] from = "AesUJTLg93cWMTSzp2snxpBJSCets89ebM".ToScriptHash(); byte[] to = "AMhbbwR8r6LuTx5okkZudvvp3LW6Fh1Y7o".ToScriptHash(); BigInterger value = new BigInteger(100000000); bool result = Contract.Call(neoScriptHash.HexToBytes(), "transfer", new Object[]{from, to, value.AsByteArray()}); return result; } } }
Build Script
Using System.Contract.Call to invoke contract:
PUSHBYTE4 00e1f505 PUSHBYTE20 4101b2a928fd88e1d976fd23c2db25a822338a08 PUSHBYTE20 fd59e6a0e3eee5cd9cea7233f01e1cc9c8b23502 PUSH3 PACK PUSHBYTES4 7472616e73666572 PUSHBYTES20 0x43cf98eddbe047e198a3e5d57006311442a0ca15 SYSCALL 0x627d5b52
C# code to build the script:
ScriptBuilder sb = new ScriptBuilder() UInt160 scriptHash = UInt160.Parse("0x43cf98eddbe047e198a3e5d57006311442a0ca15"); UInt160 from = UInt160.Parse("0xfd59e6a0e3eee5cd9cea7233f01e1cc9c8b23502"); UInt160 to = UInt160.Parse("0x4101b2a928fd88e1d976fd23c2db25a822338a08"); long value = 1000000000; sb.EmitPush(value); sb.EmitPush(to); sb.EmitPush(from); sb.Emit(OpCode.PUSH3); sb.Emit(OpCode.PACK); sb.EmitPush("transfer"); sb.EmitPush(scriptHash.ToArray()); sb.EmitSysCall(InteropService.System_Contract_Call); byte[] script = sb.ToArray();
-
unClaimGas:Get the amount of GAS unclaimed at the specified height
Example in C# contractParameters Parameter Type Description account Hash160 ScriptHash of the account end Integer The height to be queried Return Type Description Integer GAS unclaimed Fee(GAS) 0.03 using Neo.SmartContract.Framework; using Neo.SmartContract.Framework.System; public static object Main(string method, object[] args) { private static string neoScriptHash = "0x43cf98eddbe047e198a3e5d57006311442a0ca15"; if (Runtime.Trigger == TriggerType.Application) { if (method == "accountUnClaimGas") { byte[] account = "AXx1A21wcoXuVxxxggkQChxQP5EGYe6zsN".ToScriptHash(); int height = 1000000; int gas = Contract.Call(neoScriptHash.HexToBytes(), "unClaimGas", new Object[]{account, height}); return gas; } } }
Build Script
Using System.Contract.Call to invoke contract:
PUSHBYTE3 40420f PUSHBYTE20 b16c70b94928ddb62f5793fbc98d6245ee308ecd PUSH2 PACK PUSHBYTES4 756e436c61696d476173 PUSHBYTES20 0x43cf98eddbe047e198a3e5d57006311442a0ca15 SYSCALL 0x627d5b52
C# code to build the script:
ScriptBuilder sb = new ScriptBuilder() UInt160 scriptHash = UInt160.Parse("0x43cf98eddbe047e198a3e5d57006311442a0ca15"); UInt160 account = UInt160.Parse("0xb16c70b94928ddb62f5793fbc98d6245ee308ecd"); int height = 1000000 sb.EmitPush(height); sb.EmitPush(account); sb.Emit(OpCode.PUSH2); sb.Emit(OpCode.PACK); sb.EmitPush("unClaimGas"); sb.EmitPush(scriptHash.ToArray()); sb.EmitSysCall(InteropService.System_Contract_Call); byte[] script = sb.ToArray();
-
RegisterValidator:Register to become a candidate for the validator
Parameters Parameter Type Description pubKey PublicKey Public key of the account Return Type Description Boolean Result. true: sucess, false: failure Fee(GAS) 0.05 -
getRegisteredValidators:Gets the information of currently registered validators and backup nodes
Parameters none Return Type Description Array Public keys of all validators and backup nodes Fee(GAS) 1.00 -
getValidators: Get all current validators
Parameters none Return Type Description Array Public keys of all current validators Fee(GAS) 1.00 -
getNextBlockValidators: Get validators of the next block
Parameters none Return Type Description Array Public keys of validators in the next round of consensus Fee(GAS) 1.00 -
vote:Vote for to validators
Parameters Parameter Type Description account Hash160 Voter's ScriptHash pubkeys Array The public keys of validators one vote to Return Type Description Boolean Result. true: success, false: failure Fee(GAS) 5.00
Referred to as GAS, Neo's fuel token, used to pay the handling fee, in line with NEP-5 standards. Transaction costs and consensus node incentives on the Neo network are paid in GAS. The total amount of GAS is 100 million, and the minimum unit is 0.00000001. GAS is registered in the founding block but not distributed. GAS distribution mechanism: When a new block is generated, a new GAS is generated, and the generated GAS and system fees are distributed to NEO holders. The calculation and distribution of GAS are automatically performed when the NEO balance in the user's address changes. The calculation method is as follows:
Where m
is the height of the block where the user last extracted the GAS, n
is the current block height, and NEO
is the number of NEOs held by the user during m
to n
. BlockBonus
represents the amount of GAS that each block can extract. SystemFee
represents the sum of the system fees for all transactions in this block.
GasToken methods details following:
Method marked * is NEP-5 standard interface
-
name: Name of the token
Parameters none Return Type Description String Name of the token Fee(GAS) 0.00 -
symbol: Symbol of the token
Parameters none Return Type Description String Symbol of the token Fee(GAS) 0.00 -
decimals: Decimals of the token
Parameters none Return Type Description uint Decimal Fee(GAS) 0.00 -
totalSupply: Total supply
Parameters none Return Type Description BigInteger Total supply Fee(GAS) 0.01 -
balanceOf: Balance of a specific account
Parameters Parameter Type Description account Hash160 ScriptHash of the account to be queried Return Type Description Integer Balance Fee(GAS) 0.01 -
transfer: Transfer token from one account to another
Parameters Parameter Type Description from Hash160 ScriptHash of the sender's account to Hash160 ScriptHash of the receiver's account amount Integer Transferred amount Return Type Description Boolean Result, true: success,false: failure Fee(GAS) 0.08 -
getSysFeeAmount: Total system fee till a specific height
Parameters Parameter Type Description index Integer The height one want to query Return Type Description Integer Total system fee Fee(GAS) 0.01
It is the contract for configuring the consensus strategy and saves relevant parameters in the consensus process, including maximum transactions per block, maximum low-priority transactions per block, maximum low-priority-transaction size, the fee per byte, etc. The interface is described in detail below:
-
getMaxTransactionPerBlock: Get maximum transactions per block.
Parameters none Return Type Description Integer Maximum transaction count per block Fee(GAS) 0.01 -
GetFeePerByte: Get system fee per byte
Parameters none Return Type Description Integer Fee per byte Fee(GAS) 0.01 -
getBlockedAccounts: Get the addresses in the blacklist
Parameters none Return Type Description Array Address blacklist Fee(GAS) 0.01 -
setMaxTransactionsPerBlock: Set maximum transactions count per block
Parameters Parameter Type Description value Integer Maximum transactions count to be set Return Type Description Boolean Result. true: success, false: failure Fee(GAS) 0.03 -
setFeePerByte:Set system fee per byte.
Parameters Parameter Type Description value Integer System fee per byte Return Type Description Boolean Result. true: success, false: failure Fee(GAS) 0.03 -
blockAccount:Add an account into the blacklist.
Parameters Parameter Type Description account Hash160 ScriptHash of the account to be added Return Type Description Boolean Result. true: success, false: failure Fee(GAS) 0.03 -
unblockAccount:Remove an account from the blacklist.
Parameters Parameter Type Description account Hash160 ScriptHash of the account to be removed Return Type Description Boolean Result. true: success, false: failure Fee(GAS) 0.03
For more NativeContract, Please stay stunned
NativeContract is deployed in the Genesis Block by calling the interop interface of Neo.Native.Deploy, and can only be executed in the Genesis Block.
The interop service layer provides APIs for smart contracts to access the blockchain data, including the block information, transaction information, contract information, asset information, etc. Besides, the interop service layer provides the persistence capability for each contract. Each Neo smart contract is allowed to optionally enable a private storage area with the form of key-value pair. The context of persisting the storage for Neo smart contracts is determined by the callee of the contract, not the caller. The caller needs to pass its storage context to the callee (that is, authorization) before the callee can perform the read-write operation. There are two types of interop service, namely System and Neo.
When the Neo program starts, it registers a series of interop interfaces to the virtual machine for smart contract invocation.
- Service Name Each interoperation has a service name, such as
System.Contract.Call
。 - Mapping Method Each interop service corresponds to a native method. Also taking the
System.Contract.Call
as an example,private static bool Contract_Call(ApplicationEngine engine)
is the method invoked in the client. - System Fee Each interop interface has its method for system fee calculation or fixed system fee.
- Triggering Each interop interface has a supported trigger mode. For instance,
TriggerType.All
supports all triggers.
When registering, the Neo client encapsulates the service name of the interop interface, the mapping method, the method of calculating system fee, and the supported trigger mode as an InteropDescriptor and stores it in the Dictionary with the index of the hash value of the interop interface name, which is calculated as follows:
BitConverter.ToUInt32(Encoding.ASCII.GetBytes(ServiceName).Sha256(), 0)
For example the hash of System.Contract.Call
is 0x627d5b52
- Smart Contract The interop interface used in the smart contract is provided by the corresponding smart contract development framework, which can be invoked directly. When compiled, the interop interface is compiled by the compiler into the operating instructions that can be executed in NeoVM.
- Script of the Transaction The execution script can be constructed with the hash value of the interop service name and the SYSCALL operator.
Here is an example for calling the name
method of the contract 0x43cf98eddbe047e198a3e5d57006311442a0ca15
via System.Contract.Call
:
PUSH0
NEWARRAY
PUSHBYTES4 6e616d65
PUSHBYTES20 0x43cf98eddbe047e198a3e5d57006311442a0ca15
SYSCALL 0x627d5b52
C# code:
ScriptBuilder sb = new ScriptBuilder()
sb.EmitPush(0);
sb.Emit(OpCode.NEWARRAY);
sb.EmitPush(operation);
sb.EmitPush(scriptHash);
sb.EmitSysCall(InteropService.System_Contract_Call); //Call interop service
byte[] script = sb.ToArray();
In C#, it can be called as follows:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Neo;
namespace MyContract
{
public class MyContract: SmartContract
{
public static readonly byte[] GAS = HexToBytes("0xa1760976db5fcdfab2a9930e8f6ce875b2d18225");
public static object main(string method, object[] args)
{
if (method == "transferGAS") {
if (args.Length < 3) return false;
return Contract.Call(GAS, "Transfer", args);
}
}
}
}
Interop services are divided into System part and Neo part. The specific interfaces are as follows:
Description | Convert StackItem to byte array |
---|---|
Fee (GAS) | 0.001 |
Description | Convert byte array to StackItem |
---|---|
Fee (GAS) | 0.005 |
Description | Get the height of the current block |
---|---|
Fee (GAS) | 0.000004 |
Description | Get a block with the hash or block height |
---|---|
Fee (GAS) | 0.025 |
Description | Get a transaction with the txid |
---|---|
Fee (GAS) | 0.01 |
Description | Get the height of the block where the transaction included with the txid |
---|---|
Fee (GAS) | 0.01 |
Description | Get a transaction with the txid in the block |
---|---|
Fee (GAS) | 0.01 |
Description | Get a contract with the hash |
---|---|
Fee (GAS) | 0.01 |
Description | Deploy a contract |
---|---|
Explanation | The size of the script contract cannot exceed 1MB and the size of the manifest cannot exceed 2KB |
Fee (GAS) | (Script.Size + Manifest.Size) * GasPerByte |
Description | Upgrade a contract |
---|---|
Explanation | The size of the script contract cannot exceed 1MB and it cannot be deployed before. The size of the manifest cannot exceed 2KB. The old contract will be destroyed after the upgrade. |
Fee (GAS) | (Script.Size + Manifest.Size) * GasPerByte |
Description | Destroy a contract |
---|---|
Fee (GAS) | 0.01 |
Description | Invoke a contract |
---|---|
Fee (GAS) | 0.01 |
Description | Invoke a contract with the Flag |
---|---|
Fee (GAS) | 0.01 |
Description | Check whether the contract is a standard contract |
---|---|
Fee (GAS) | 0.0003 |
Description | Create a enumerator |
---|---|
Fee (GAS) | 0.000004 |
Description | Check if the enumerator has more element |
---|---|
Fee (GAS) | 0.01 |
Description | Get the current value of the enumerator |
---|---|
Fee (GAS) | 0.000004 |
Description | Concat two enumerators |
---|---|
Fee (GAS) | 0.000004 |
Description | Create an iterator |
---|---|
Fee (GAS) | 0.000004 |
Description | Get the current key of the iterator |
---|---|
Fee (GAS) | 0.000004 |
Description | Get all keys of the iterator |
---|---|
Fee (GAS) | 0.000004 |
Description | Get all values of the iterator |
---|---|
Fee (GAS) | 0.000004 |
Description | Concat two iterators |
---|---|
Fee (GAS) | 0.000004 |
Description | Serialize a stack item to byte array |
---|---|
Fee (GAS) | 0.001 |
Description | Convert json object to stack item |
---|---|
Fee (GAS) | 0.005 |
Description | Get the platform information of the contract being executed |
---|---|
Fee (GAS) | 0.0000025 |
Description | Get the triggering condition of the contract |
---|---|
Fee (GAS) | 0.0000025 |
Description | Get the timestamp of the current block |
---|---|
Fee (GAS) | 0.0000025 |
Description | Get the script container of the contract |
---|---|
Fee (GAS) | 0.0000025 |
Description | Get the script hash of the executing contract |
---|---|
Fee (GAS) | 0.000004 |
Description | Get the script hash of the calling contract |
---|---|
Fee (GAS) | 0.000004 |
Description | Get the script hash of the entry contract |
---|---|
Fee (GAS) | 0.000004 |
Description | Verify whether the container calling the contract is signed by the script hash of the specific account |
---|---|
Fee (GAS) | 0.0003 |
Description | Get invocation count of the current contract |
---|---|
Fee (GAS) | 0.000004 |
Description | Record the log |
---|---|
Fee (GAS) | 0.005 |
Description | Notify the client executing the contract |
---|---|
Fee (GAS) | 0.01 |
Description | Get notifications of a contract |
---|---|
Fee (GAS) | 0.0001 |
Description | Get the unconsumed GAS |
---|---|
Fee (GAS) | 0.00000400 |
Description | Get storage context of the current contract |
---|---|
Explanation | the IsReadOnly flag of StorageContext is false |
Fee (GAS) | 0.000004 |
Description | Get storage context of the current contract in read-only mode |
---|---|
Explanation | the IsReadOnly flag of StorageContext is true |
Fee (GAS) | 0.000004 |
Description | Get value from the storage by key |
---|---|
Fee (GAS) | 0.01 |
Description | Find the data in the storage area of the current storage context with the specified prefix |
---|---|
Fee (GAS) | 0.01 |
Description | Put key-value into storage based on the storage context |
---|---|
Fee (GAS) | (Key.Size + Value.Size) * GasPerByte |
Description | Put Key-Value into the storage based on the storage context and the flag |
---|---|
Explanation | The parameter StorageFlags defines the attribute of the written data. The default value is None, indicating that data can be read and written. If it is Constant, the data cannot be modified or deleted once it is written to the storage area. |
Fee (GAS) | (Key.Size + Value.Size) * GasPerByte |
Description | Delete the stored Key-Value data from the storage area by the Key |
---|---|
Explanation | Data cannot be deleted if its StorageFlags is Constant |
Fee (GAS) | 0.01 |
Description | Set the current context to read-only mode |
---|---|
Explanation | Set the IsReadOnly flag of the StorageContext to true |
Fee (GAS) | 0.000004 |
Description | Deploy and initialize all native contracts |
---|---|
Explanation | It can only be invoked in the genesis block |
Fee (GAS) | 0 |
Method Name | Description | Fee (GAS) |
---|---|---|
name | Get token name, ie: NEO | 0 |
symbol | Get token symbol, ie: neo | 0 |
decimals | Get decimals | 0 |
totalSupply | Get the total supply | 0.01 |
balanceOf | Get balance of the token | 0.01 |
transfer | Transfer the token | 0.08 |
registerValidator | Register to be a validator | 0.05 |
vote | Vote | 5 |
getRegisteredValidators | Get registered validators | 1 |
getValidators | Get validators | 1 |
unclaimedGas | Get unclaimed Gas | 0.03 |
Method Name | Description | Fee (GAS) |
---|---|---|
name | Get token name, ie: GAS | 0 |
symbol | Get token symbol, ie: gas | 0 |
decimals | Get decimals | 0 |
totalSupply | Get the total supply | 0.01 |
balanceOf | Get balance of the token | 0.01 |
transfer | Transfer the token | 0.08 |
getSysFeeAmount | Get the amount of the system fee | 0.01 |
Method Name | Description | Fee (GAS) |
---|---|---|
getMaxTransactionsPerBlock | Get max transaction number per block | 0.01 |
getMaxBlockSize | Get max block size | 0.01 |
getFeePerByte | Get fee per byte | 0.01 |
setMaxBlockSize | Set the max block size | 0.03 |
getBlockedAccounts | Get blocked accounts | 0.01 |
setMaxTransactionsPerBlock | Set max transaction per block | 0.03 |
setFeePerByte | Set fee per byte | 0.03 |
blockAccount | Set blocked accounts | 0.03 |
unblockAccount | Unblock accounts | 0.03 |
Description | Verify signature of the current script container by public key |
---|---|
Fee (GAS) | 0.01 |
Description | Verify the multiple signature of the current script container by public key |
---|---|
Fee (GAS) | 0.01 * n |
When writing a contract, you can invoke other contracts through the interop service provided by the development frameworkSystem.Contract.Call. Here is an example in C#:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Neo;
namespace MyContract
{
public class MyContract: SmartContract
{
public static readonly byte[] GAS = HexToBytes("0xa1760976db5fcdfab2a9930e8f6ce875b2d18225");
public static object main(string method, object[] args)
{
if (method == "transferGAS") {
if (args.Length < 3) return false;
return Contract.Call(GAS, "transfer", args);
}
}
}
}
In the case of the execution script constructed manually, the contract should be invoked with the interop service System.Contract.Call and the ScriptHash of the contract. Please refer to How to use interop services
Here is an example for invoking method transfer
of the contract 0x43cf98eddbe047e198a3e5d57006311442a0ca15
via System.Contract.Call
:
PUSHBYTES4 0x00e1f505
PUSHBYTES20 0xfb5fd311a3ae2b2c8ab6b63c10502f9cf58ebeed
PUSHBYTES20 0xa6b9b510a67009e61f4f95115c188437f76dd2d0
PUSH3
PACK //The third parameter, parameter list
PUSHBYTES4 0x6e616d65 //The second parameter, method name
PUSHBYTES20 0x43cf98eddbe047e198a3e5d57006311442a0ca15 //The first parameter, contract's ScriptHash
SYSCALL 0x627d5b52 //Calling interop service by hash
C# code:
ScriptBuilder sb = new ScriptBuilder();
UInt160 scriptHash = UInt160.Parse("0x43cf98eddbe047e198a3e5d57006311442a0ca15");
string operation = "transfer";
ContractParameter from = new ContractParameter(ContractParameterType.Hash160);
ContractParameter to = new ContractParameter(ContractParameterType.Hash160);
ContractParameter value = new ContractParameter(ContractParameterType.Integer);
from.SetValue("0xa6b9b510a67009e61f4f95115c188437f76dd2d0");
to.SetValue("0xfb5fd311a3ae2b2c8ab6b63c10502f9cf58ebeed");
value.SetValue("100000000");
sb.EmitAppCall(scriptHash, operation, new ContractParameter[]{from, to, value});
byte[] script = sb.ToArray();
public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params ContractParameter[] args)
{
for (int i = args.Length - 1; i >= 0; i--)
sb.EmitPush(args[i]);
sb.EmitPush(args.Length);
sb.Emit(OpCode.PACK);
sb.EmitPush(operation);
sb.EmitPush(scriptHash.ToArray());
sb.EmitSysCall(InteropService.System_Contract_Call);
return sb;
}
You can upgrade the contract to modify the logic of the contract and keep the data in the storage area after the contract is deployed, but you need to implement the upgrade interface in the old contract through invoking the Neo.Contract.Update method.
When the upgrade interface is called in the old contract, the method will build a new smart contract based on the parameters passed in. If the old contract has a storage area, the storage area will be transferred to the new contract. After the upgrade, the old contract will be deleted, as well as its storage area (if any). The old contract will then be unavailable and instead, the hash of the new contract should be used.
C# code:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Neo;
using Neo.SmartContract.Framework.System;
public static bool Main(string method, params object[] args){
if (method == "update") {
if (args.Length < 2) return false;
if (!Runtime.CheckWitness()) return false;
byte[] newScript = args[0];
string manifest = (string)args[1];
update(newScript, manifest);
return true;
}
return true;
}
void update(byte[] newScript, string manifest)
{
Contract.Update(newScript, manifest);
}
The smart contract supports the destruction operation after the release, but the interface for destroying the contract needs to be reserved in the old contract, mainly involved in the invocation of System.Contract.Destroy
method.
C# code:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.System;
public static bool Main(string method, params object[] args){
if (method == "destroy") {
destroy();
}
return true;
}
void destroy(){
Contract.Destroy();
}
Click here to see the Chinese edition of the Smart Contract