The contract is a JavaScript code, with a standard (ECMAScript as specified in ECMA-262). The initialization function of the contract is init, and the entry function of the execution is the main function. You must have the definition of the init and main functions in the contract code. The input argument to this function is the string input, which is specified when the contract is called.
For details of the smart contract, refer to Introduction to Smart Contract.
For details of the smart contract syntax, refer to Syntax in Smart Contract.
This section introduces three Java-based smart contract development scenarios, and all scenarios are associated.
Scenario One mainly implements the contract creation function.
Scenario Two mainly implements contract triggering function.
Scenario Three mainly implements contract query function.
The scenarios are the smart contract codes.
'use strict';let globalAttribute = {};constglobalAttributeKey='global_attribute';functionmakeAllowanceKey(owner, spender){return'allow_'+ owner +'_to_'+ spender;}functionapprove(spender, value){assert(addressCheck(spender) ===true,'Arg-spender is not a valid address.');assert(stoI64Check(value) ===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0') >0,'Arg-value of spender '+ spender +' must be greater than 0.');let key =makeAllowanceKey(sender, spender);storageStore(key, value);tlog('approve', sender, spender, value);returntrue;}functionallowance(owner, spender){assert(addressCheck(owner) ===true,'Arg-owner is not a valid address.');assert(addressCheck(spender) ===true,'Arg-spender is not a valid address.');let key =makeAllowanceKey(owner, spender);let value =storageLoad(key);assert(value !==false,'Failed to get the allowance given to '+ spender +' by '+ owner +' from metadata.');return value;}functiontransfer(to, value){assert(addressCheck(to) ===true,'Arg-to is not a valid address.');assert(stoI64Check(value) ===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0') >0,'Arg-value must be greater than 0.');if(sender === to) {tlog('transfer', sender, to, value); returntrue; }let senderValue =storageLoad(sender);assert(senderValue !==false,'Failed to get the balance of '+ sender +' from metadata.'); assert(int64Compare(senderValue, value) >= 0, 'Balance:' + senderValue + ' of sender:' + sender + ' < transfer value:' + value + '.');
let toValue =storageLoad(to); toValue = (toValue ===false) ? value :int64Add(toValue, value); storageStore(to, toValue); senderValue =int64Sub(senderValue, value);storageStore(sender, senderValue);tlog('transfer', sender, to, value);returntrue;}functiontransferFrom(from, to, value){assert(addressCheck(from) ===true,'Arg-from is not a valid address.');assert(addressCheck(to) ===true,'Arg-to is not a valid address.');assert(stoI64Check(value) ===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0') >0,'Arg-value must be greater than 0.');if(from === to) {tlog('transferFrom', sender, from, to, value);returntrue; }let fromValue =storageLoad(from);assert(fromValue !==false,'Failed to get the value, probably because '+ from +' has no value.');assert(int64Compare(fromValue, value) >=0, from +' Balance:'+ fromValue +' < transfer value:'+ value +'.');let allowValue =allowance(from, sender); assert(int64Compare(allowValue, value) >= 0, 'Allowance value:' + allowValue + ' < transfer value:' + value + ' from ' + from + ' to ' + to + '.');
let toValue =storageLoad(to); toValue = (toValue ===false) ? value :int64Add(toValue, value); storageStore(to, toValue); fromValue =int64Sub(fromValue, value);storageStore(from, fromValue);let allowKey =makeAllowanceKey(from, sender); allowValue =int64Sub(allowValue, value);storageStore(allowKey, allowValue);tlog('transferFrom', sender, from, to, value);returntrue;}functionbalanceOf(address){assert(addressCheck(address) ===true,'Arg-address is not a valid address.');let value =storageLoad(address);assert(value !==false,'Failed to get the balance of '+ address +' from metadata.');return value;}functioninit(input_str){let params =JSON.parse(input_str).params;assert(stoI64Check(params.totalSupply) ===true&¶ms.totalSupply >0&&typeofparams.name ==='string'&¶ms.name.length>0&&typeofparams.symbol ==='string'&¶ms.symbol.length>0&&typeofparams.decimals ==='number'&¶ms.decimals >=0,'Failed to check args');globalAttribute.totalSupply =params.totalSupply;globalAttribute.name =params.name;globalAttribute.symbol =params.symbol;globalAttribute.version ='Contract20';globalAttribute.decimals =params.decimals;storageStore(globalAttributeKey,JSON.stringify(globalAttribute));storageStore(sender,globalAttribute.totalSupply);}functionmain(input_str){let input =JSON.parse(input_str);if(input.method ==='transfer'){transfer(input.params.to,input.params.value); }elseif(input.method ==='transferFrom'){transferFrom(input.params.from,input.params.to,input.params.value); }elseif(input.method ==='approve'){approve(input.params.spender,input.params.value); }else{throw'<Main interface passes an invalid operation type>'; }}functionquery(input_str){let result = {};let input =JSON.parse(input_str);if(input.method ==='tokenInfo'){ globalAttribute =JSON.parse(storageLoad(globalAttributeKey));result.tokenInfo = globalAttribute; }elseif(input.method ==='allowance'){result.allowance =allowance(input.params.owner,input.params.spender); }elseif(input.method ==='balanceOf'){result.balance =balanceOf(input.params.address); }else{throw'<Query interface passes an invalid operation type>'; }returnJSON.stringify(result);}
Scenario One
An asset issuer issues smart contract assets, the total amount of which is 1 billion, the issuance code is CGO, and the name is Contract Global. The details are as follows:
Field
Required?
Example
Description
name
Yes
Contract Global
token name
symbol
Yes
CGO
token code
decimals
Yes
8
Precision
totalSupply
Yes
1000000000
total amount
The specific execution process of this scenario includes Compressing Text, Creating SDK Instances, Creating the Asset Issuer Account, Activating the Asset Issuer Account, Obtaining the Serial Number of the Asset Issuer Account, Assembling the Creation of the Contract Account and the CGO Token Issuance, Serializing the Transaction, Signing the Transaction, Sending the Transaction, and Querying Whether the Transaction Was Executed Successfully.
Compressing Text
Open the online text compression page: tool from the third party , copy the verified smart contract code to the edit box on the page, then click the Compress button to copy the compressed string, as shown below:
Creating SDK Instances-1
Create an instance and set the url (the IP and port of a deployed node).
In the Zetrix network, each block is generated every 10 seconds, and each transaction requires only one confirmation to get the final state of the transaction.
Creating the Asset Issuer Account
The code to create the asset issuer account is as follows:
Note: An account created in this way is an account that is not activated.
Activating the Asset Issuer Account
When the account is not activated, it needs to be activated by an activated (chained) account. Please skip this section if your account has been activated.
Note: Use an activated account to transfer gas to an inactive account.
Obtaining the Serial Number of the Asset Issuer Account-1
Each account maintains its own serial number, which starts from 1 and is incremented. A serial number marks a transaction for that account. The code to obtain the serial number of the asset issuer account is as follows:
Note: If an account is not queried, it means that the account is not activated.
Assembling the Creation of the Contract Account and the CGO Token Issuance
The code assigns the compressed contract code to the payload variable. The specific code is as follows:
publicBaseOperation[] buildOperations() { // The account address to issue apt1.0 token String createContractAddress ="ztxSXVGt5ujjAe4hr11VqhjWpJdKqn6QfDVUX"; // Contract account initialization Gas,the unit is UGas,and 1 Gas = 10^8 UGas Long initBalance =ToBaseUnit.ToUGas("0.01"); // The token name String name ="Contract Global"; // The token code String symbol ="CGO"; // The token total supply, which includes the dicimals.// If decimals is 8 and you want to issue 10 assets now, the nowSupply must be 10 * 10 ^ 8, like below.String totalSupply ="1000000000";// The token decimals Integer decimals =8; // Contract code String payload = "'use strict';let globalAttribute={};const globalAttributeKey='global_attribute';function makeAllowanceKey(owner,spender){return'allow_'+owner+'_to_'+spender;}function approve(spender,value){assert(addressCheck(spender)===true,'Arg-spender is not a valid address.');assert(stoI64Check(value)===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0')>0,'Arg-value of spender '+spender+' must be greater than 0.');let key=makeAllowanceKey(sender,spender);storageStore(key,value);tlog('approve',sender,spender,value);return true;}function allowance(owner,spender){assert(addressCheck(owner)===true,'Arg-owner is not a valid address.');assert(addressCheck(spender)===true,'Arg-spender is not a valid address.');let key=makeAllowanceKey(owner,spender);let value=storageLoad(key);assert(value!==false,'Failed to get the allowance given to '+spender+' by '+owner+' from metadata.');return value;}function transfer(to,value){assert(addressCheck(to)===true,'Arg-to is not a valid address.');assert(stoI64Check(value)===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0')>0,'Arg-value must be greater than 0.');if(sender===to){tlog('transfer',sender,to,value);return true;}let senderValue=storageLoad(sender);assert(senderValue!==false,'Failed to get the balance of '+sender+' from metadata.');assert(int64Compare(senderValue,value)>=0,'Balance:'+senderValue+' of sender:'+sender+' < transfer value:'+value+'.');let toValue=storageLoad(to);toValue=(toValue===false)?value:int64Add(toValue,value);storageStore(to,toValue);senderValue=int64Sub(senderValue,value);storageStore(sender,senderValue);tlog('transfer',sender,to,value);return true;}function transferFrom(from,to,value){assert(addressCheck(from)===true,'Arg-from is not a valid address.');assert(addressCheck(to)===true,'Arg-to is not a valid address.');assert(stoI64Check(value)===true,'Arg-value must be alphanumeric.');assert(int64Compare(value,'0')>0,'Arg-value must be greater than 0.');if(from===to){tlog('transferFrom',sender,from,to,value);return true;}let fromValue=storageLoad(from);assert(fromValue!==false,'Failed to get the value, probably because '+from+' has no value.');assert(int64Compare(fromValue,value)>=0,from+' Balance:'+fromValue+' < transfer value:'+value+'.');let allowValue=allowance(from,sender);assert(int64Compare(allowValue,value)>=0,'Allowance value:'+allowValue+' < transfer value:'+value+' from '+from+' to '+to+'.');let toValue=storageLoad(to);toValue=(toValue===false)?value:int64Add(toValue,value);storageStore(to,toValue);fromValue=int64Sub(fromValue,value);storageStore(from,fromValue);let allowKey=makeAllowanceKey(from,sender);allowValue=int64Sub(allowValue,value);storageStore(allowKey,allowValue);tlog('transferFrom',sender,from,to,value);return true;}function balanceOf(address){assert(addressCheck(address)===true,'Arg-address is not a valid address.');let value=storageLoad(address);assert(value!==false,'Failed to get the balance of '+address+' from metadata.');return value;}function init(input_str){let params=JSON.parse(input_str).params;assert(stoI64Check(params.totalSupply)===true&¶ms.totalSupply>0&&typeof params.name==='string'&¶ms.name.length>0&&typeof params.symbol==='string'&¶ms.symbol.length>0&&typeof params.decimals==='number'&¶ms.decimals>=0,'Failed to check args');globalAttribute.totalSupply=params.totalSupply;globalAttribute.name=params.name;globalAttribute.symbol=params.symbol;globalAttribute.version='Contract20';globalAttribute.decimals=params.decimals;storageStore(globalAttributeKey,JSON.stringify(globalAttribute));storageStore(sender,globalAttribute.totalSupply);}function main(input_str){let input=JSON.parse(input_str);if(input.method==='transfer'){transfer(input.params.to,input.params.value);}else if(input.method==='transferFrom'){transferFrom(input.params.from,input.params.to,input.params.value);}else if(input.method==='approve'){approve(input.params.spender,input.params.value);}else{throw'<Main interface passes an invalid operation type>';}}function query(input_str){let result={};let input=JSON.parse(input_str);if(input.method==='tokenInfo'){globalAttribute=JSON.parse(storageLoad(globalAttributeKey));result.tokenInfo=globalAttribute;}else if(input.method==='allowance'){result.allowance=allowance(input.params.owner,input.params.spender);}else if(input.method==='balanceOf'){result.balance=balanceOf(input.params.address);}else{throw'<Query interface passes an invalid operation type>';}return JSON.stringify(result);}";
// Init initInput JSONObject initInput =newJSONObject(); JSONObject params =newJSONObject(); params.put("name", name); params.put("symbol", symbol); params.put("decimals", decimals); params.put("totalSupply", totalSupply);initInput.put("params", params); // Build create contract operation ContractCreateOperation contractCreateOperation =newContractCreateOperation(); contractCreateOperation.setSourceAddress(createContractAddress); contractCreateOperation.setInitBalance(initBalance); contractCreateOperation.setPayload(payload); contractCreateOperation.setInitInput(initInput.toJSONString()); contractCreateOperation.setMetadata("create contract"); BaseOperation[] operations = { contractCreateOperation }; return operations; }
Serializing the Transaction-1
Serializing transactions is for the convenience network transmission.
Note:
feeLimit: the maximum transaction fee that the originator of this transaction will pay for this transaction. Please fill in 10.08Gas for the issuance of this asset.
nonce: the transaction serial number of the originator of this transaction, which is obtained by adding 1 to the nonce value of the current account.
The specific code of serializing transactions is as follows. The parameter nonce in the example is the account serial number obtained by calling getAccountNonce, and the parameter operations is the asset issuance operation obtained by calling buildOperations.
publicStringseralizeTransaction(Long nonce,BaseOperation[] operations) { String transactionBlob =null; // The account address to create contracts and issue assets String senderAddresss ="ztxSXVGt5ujjAe4hr11VqhjWpJdKqn6QfDVUX"; // The gasPrice is fixed at 1000L, the unit is UGas Long gasPrice =1000L; // Set up the maximum cost 10.08Gas Long feeLimit =ToBaseUnit.ToUGas("10.08"); // Nonce should add 1 nonce +=1; // Build transaction Blob TransactionBuildBlobRequest transactionBuildBlobRequest =newTransactionBuildBlobRequest(); transactionBuildBlobRequest.setSourceAddress(senderAddresss); transactionBuildBlobRequest.setNonce(nonce); transactionBuildBlobRequest.setFeeLimit(feeLimit); transactionBuildBlobRequest.setGasPrice(gasPrice); for (int i =0; i <operations.length; i++) { transactionBuildBlobRequest.addOperation(operations[i]); } TransactionBuildBlobResponse transactionBuildBlobResponse = sdk.getTransactionService().buildBlob(transactionBuildBlobRequest);
if (transactionBuildBlobResponse.getErrorCode() ==0) { transactionBlob =transactionBuildBlobResponse.getResult().getTransactionBlob(); } else { System.out.println("error: "+transactionBuildBlobResponse.getErrorDesc()); } return transactionBlob; }
All transactions need to be signed, and a transaction will not take effect until it is signed. The signature result includes signature data and a public key. The specific code for signing transactions is as follows. The parameter transactionBlob in the example is the serialized transaction string obtained by calling seralizeTransaction.
Send the serialized transaction and the signature to Zetrix. The specific code for sending the transaction is as follows. The parameter transactionBlob in the example is the serialized transaction string obtained by calling seralizeTransaction, and signatures is the signature data obtained by calling signTransaction.
Querying Whether the Transaction Was Executed Successfully-1
The following code shows how to query by calling the interface. The parameter txHash in this example is the transaction hash (the unique identifier of the transaction) obtained by calling submitTransaction.
Objects already exist, such as repeated transactions
4
Objects do not exist, such as null account, transactions and blocks etc.
5
Transactions expired. It means the transaction has been removed from the buffer, but it still has probability to be executed.
7
Math calculation is overflown
20
The expression returns false. It means the TX failed to be executed currently, but it still has probability to be executed in the following blocks .
21
The syntax of the expression returns are false. It means that the TX must fail.
90
Invalid public key
91
Invalid private key
92
Invalid assets
93
The weight of the signature does not match the threshold of the operation.
94
Invalid address
97
Absent operation of TX
98
Over 100 operations in a single transaction
99
Invalid sequence or nonce of TX
100
Low reserve in the account
101
Sender and receiver accounts are the same
102
The target account already exists
103
Accounts do not exist
104
Low reserve in the account
105
Amount of assets exceeds the limitation ( int64 )
106
Insufficient initial reserve for account creation
111
Low transaction fee
114
TX buffer is full
120
Invalid weight
121
Invalid threshold
144
Invalid data version of metadata
146
Exceeds upper limitation
151
Failure in contract execution
152
Failure in syntax analysis
153
The depth of contract recursion exceeds upper limitation
154
The TX submitted from the contract exceeds upper limitation
155
Contract expired
160
Fail to insert the TX into buffer
11060
BlockNumber must be bigger than 0
11007
Failed to connect to the network
12001
Request parameter cannot be null
20000
System error
Scenario Two
This example mainly implements contract triggering function.
The asset issuer ztxSXVGt5ujjAe4hr11VqhjWpJdKqn6QfDVUX is assigned to himself 20000 CGO on Zetrix through the smart contract account ztxSfaizkSkNovPtVCh5X2mwtd649R6pG9YS4, and transfers 10000 CGO to another account ztxSqAo6moyR5bYyTnKQXgkrzyiSBKXSwKHu4.
The specific implementation process in this scenario includes: Creating SDK Instances, Obtaining the Serial Number of the Asset Issuer Account, Assembling CGO Allocation and CGO Transfer, Serializing Transactions, Signing Transactions, Sending Transactions, and Querying whether the Transaction Was Executed Successfully.
Creating SDK Instances-2
Create an instance and set the url (the IP and port of a deployed node).
In the Zetrix network, each block is generated every 10 seconds, and each transaction requires only one confirmation to get the final state of the transaction.
Environment description:
Network Environment
IP
Experience Zetrix
seed1-node.zetrix.com
Obtaining the Serial Number of the Asset Issuer Account-2
Each account maintains its own serial number, which starts from 1 and is incremented. A serial number marks a transaction for that account. The code to obtain the serial number of the asset issuer account is as follows:
This section contains two operations: allocating CGO and transferring CGO. The following is the sample code:
publicBaseOperation[] buildOperations() { // The account address to issue apt1.0 token String invokeAddress ="ztxSXVGt5ujjAe4hr11VqhjWpJdKqn6QfDVUX"; // The contract address String contractAddress ="ztxSfaizkSkNovPtVCh5X2mwtd649R6pG9YS4"; // The destination address String destAddress ="ztxSqAo6moyR5bYyTnKQXgkrzyiSBKXSwKHu4"; // The amount to be assigned String assignAmount ="20000"; // The amount to be transfered String transferAmount ="10000";// build assign method input JSONObject assignInput =newJSONObject(); assignInput.put("method","assign"); JSONObject assignParams =newJSONObject(); assignParams.put("to", invokeAddress); assignParams.put("value", assignAmount); assignInput.put("params", assignParams); // build send gas operation to assign CGO ContractInvokeByGasOperation assignOperation =newContractInvokeByGasOperation(); assignOperation.setSourceAddress(invokeAddress); assignOperation.setContractAddress(contractAddress); assignOperation.setGasAmount(0L); assignOperation.setInput(assignInput.toJSONString());// build transfer method input JSONObject transferInput =newJSONObject(); transferInput.put("method","transfer"); JSONObject transferParams =newJSONObject(); transferParams.put("to", destAddress); transferParams.put("value", transferAmount); transferInput.put("params", transferParams);// build send gas operation to transfer CGO ContractInvokeByGasOperation transferOperation =newContractInvokeByGasOperation(); transferOperation.setSourceAddress(invokeAddress); transferOperation.setContractAddress(contractAddress); transferOperation.setGasAmount(0L); transferOperation.setInput(transferInput.toJSONString()); BaseOperation[] operations = { assignOperation, transferOperation }; return operations; }
Serializing Transactions-2
Serializing transactions for the convenience of network transmission.
Note:
feeLimit: the maximum transaction fee that the originator of this transaction will pay for this transaction.To create a contract account and issue token operation, please fill in 0.02 Gas.
nonce: the transaction serial number of the originator of this transaction, which is obtained by adding 1 to the nonce value of the current account.
The specific code of serializing the transaction is as follows. The parameter nonce in the example is the account serial number obtained by calling getAccountNonce, and the parameter operations is the asset issuance operation obtained by calling buildOperations. The following is the sample code for serializing the transaction:
publicStringseralizeTransaction(Long nonce,BaseOperation[] operations) { String transactionBlob =null; // The account address to create contract and issue token String senderAddresss ="ztxSXVGt5ujjAe4hr11VqhjWpJdKqn6QfDVUX"; // The gasPrice is fixed at 1000L, the unit is UGas Long gasPrice =1000L; // Set up the maximum cost 10.08Gas Long feeLimit =ToBaseUnit.ToUGas("0.02"); // Nonce should add 1 nonce +=1; // Build transaction Blob TransactionBuildBlobRequest transactionBuildBlobRequest =newTransactionBuildBlobRequest(); transactionBuildBlobRequest.setSourceAddress(senderAddresss); transactionBuildBlobRequest.setNonce(nonce); transactionBuildBlobRequest.setFeeLimit(feeLimit); transactionBuildBlobRequest.setGasPrice(gasPrice); for (int i =0; i <operations.length; i++) { transactionBuildBlobRequest.addOperation(operations[i]); } TransactionBuildBlobResponse transactionBuildBlobResponse = sdk.getTransactionService().buildBlob(transactionBuildBlobRequest);
if (transactionBuildBlobResponse.getErrorCode() ==0) { transactionBlob =transactionBuildBlobResponse.getResult().getTransactionBlob(); } else { System.out.println("error: "+transactionBuildBlobResponse.getErrorDesc()); } return transactionBlob; }
All transactions need to be signed, and a transaction will not take effect until it is signed. The signature result includes signature data and a public key. The specific code for signing transactions is as follows. The parameter transactionBlob in the example is the serialized transaction string obtained by calling seralizeTransaction.
Send the serialized transaction and signature to Zetrix. The specific code for sending transactions is as follows. The parameter transactionBlob in the example is the serialized transaction string obtained by calling seralizeTransaction, and the parameter signatures is the signature data obtained by calling signTransaction.
Querying whether the Transaction Was Executed Successfully-2
The following code shows how to query by calling the interface. The parameter txHash in this example is the transaction hash (the unique identifier of the transaction) obtained by calling submitTransaction.
Objects already exist, such as repeated transactions
4
Objects do not exist, such as null account, transactions and blocks etc.
5
Transactions expired. It means the transaction has been removed from the buffer, but it still has probability to be executed.
7
Math calculation is overflown
20
The expression returns false. It means the TX failed to be executed currently, but it still has probability to be executed in the following blocks .
21
The syntax of the expression returns are false. It means that the TX must fail.
90
Invalid public key
91
Invalid private key
92
Invalid assets
93
The weight of the signature does not match the threshold of the operation.
94
Invalid address
97
Absent operation of TX
98
Over 100 operations in a single transaction
99
Invalid sequence or nonce of TX
100
Low reserve in the account
101
Sender and receiver accounts are the same
102
The target account already exists
103
Accounts do not exist
104
Low reserve in the account
105
Amount of assets exceeds the limitation ( int64 )
106
Insufficient initial reserve for account creation
111
Low transaction fee
114
TX buffer is full
120
Invalid weight
121
Invalid threshold
144
Invalid data version of metadata
146
Exceeds upper limitation
151
Failure in contract execution
152
Failure in syntax analysis
153
The depth of contract recursion exceeds upper limitation
154
The TX submitted from the contract exceeds upper limitation
155
Contract expired
160
Fail to insert the TX into buffer
11060
BlockNumber must be bigger than 0
11007
Failed to connect to the network
12001
Request parameter cannot be null
20000
System error
Scenario Three
This example mainly implements contract query function.
Check the CGO balance of the account ztxSfaizkSkNovPtVCh5X2mwtd649R6pG9YS4 via the smart contract account ztxSqAo6moyR5bYyTnKQXgkrzyiSBKXSwKHu4 on Zetrix.
This section mainly introduces Creating SDK Instances and Querying Balance.
Creating SDK Instances-3
Create an instance and set the url (the IP and port of a deployed node).
In the Zetrix network, each block is generated every 10 seconds, and each transaction requires only one confirmation to get the final state of the transaction. Environment description:
Network Environment
IP
Experience Zetrix
seed1-node.zetrix.com
Querying Balance
The sample code for querying the balance is as follows: