Idena smart contracts
There are two types of smart contracts in Idena:
- Custom contracts that can be developed and deployed by any developer
- Predefined contracts built-in into the Idena node code (can be changed with hard fork updates)
Custom contracts are executed in Wasmer runtime. Any language that compiles to WebAssembly (Wasm) can be used for developing contracts. Currently you can use TypeScript-like language AssemblyScript and compile your contracts into WebAssembly (see Quick start). For developing contracts in AssemblyScript Idena-sdk-as is recommended.
Contract transactions
There are three types of transactions to deal with Idena contracts:
1. DeployContractTx
Deploy custom contract
DeployContractTx
transaction deploys a custom contract specified by the code
and nonce
and executes the contract class constructor. Fields of DeployContractTx
transaction for creating a custom contract:
from
: sender addresscode
: compiled WebAssembly code (see Quick start to build a simple custom contract)args
: dynamic list of parameters of constructor which is specific to a particular contractnonce
: unique nonce that allows you to generate unique addresses for contracts with the samecode
andargs
maxFee
: must cover a sum oftxCost
+gasCost
(see more aboutmaxFee
)
Please note that that maximum size of the transaction payload is MaxPayloadSize
= 3 MB
Deploy predefined contract
DeployContractTx
transaction deploys a predefined contract specified by the codeHash
and executes the contract class constructor. Fields of DeployContractTx
transaction for creating a predefined contract:
from
: sender addressamount
: specifies amount of coins that will be blocked in the stake of the contractcodeHash
: contract codeargs
: dynamic list of parameters of constructor which is specific to a particular contractmaxFee
: must cover a sum oftxCost
+gasCost
(see more aboutmaxFee
)
codeHash
specifies the algorithm of a predefined contract:
0x01
: TimeLock0x02
: OracleVoting (integrated into Idena app)0x03
: OracleLock0x04
: RefundableOracleLock0x05
: Multisig
You can find the code of predefined contracts here
amount
specifies amount of iDNA transfered to the contract stake. Minimum contract stake is gasPrice
* 3,000,000
. If the specified amount is below the minimum stake then an error message will be returned:
"error": {
"code": -32000,
"message": "invalid amount"
}
2. CallContractTx
CallContractTx
transaction executes specified public method of the contract class. Fields of the CallContractTx
transaction:
from
: sender addresscontract
: smart contract addressmethod
: name of the public contract methodamount
: amount of coins transferred to the smart contract addressargs
: dynamic list of parameters which is specific to a particular method
3. TerminateContractTx
TerminateContractTx
is available only for predefined contracts
TerminateContractTx
transaction removes the contract's data from the Idena blockchain state. 50% of the stake is burnt. Another 50% of the stake is transferred to the creator of the smart contract. The contract might be terminated according to its internal rules. For instance OracleVoting
smart contract can be terminated after a termination delay once the public voting is finished. Termination delay is proportional to the amount of coins blocked at the smart contract stake. Termination delay, days = round( (NetworkSize * Stake) ^ 1/3 )
Fields of the TerminateContractTx
transaction:
from
: sender addresscontract
: smart contract addressargs
: dynamic list of parameters which is specific to a particular predefined smart contract
TerminateContractTx
is not available for custom contracts. In the future to minimize the state of the Idena node a mechanism that suspends inactive contracts will be introduced. If a contract is not used (by validated users) then it might be suspended. After N epochs the state of suspended contract might be deleted. Only Merkle root will be saved so anyone who saved the state of the suspended contract offchain could initiate its recovery.
Contract addresses
Custom contracts address
The address of the custom contract is calculated as a hash from code hash, protobuf packed args and deploy attachment nonce. The contract address is the last 20 bytes of the hash (see example)
You can call contract_estimateDeploy
to calculate the address of the future contract.
Predefined contract address
The address of the predefined contract is calculated as a hash from tx sender, tx epoch and tx account nonce. The contract address is the last 20 bytes of the hash.
You can calculate the address of the predefined contract before it will be deployed as following
const addr = toBuffer(hexToUint8Array(state.address));
const epoch = int16ToBuffer(parseInt(state.epoch));
const nonce = int32ToBuffer(parseInt(state.nonce));
const res = [...addr, ...epoch, ...nonce];
const hash = sha3.keccak_256.array(res);
response.address = toHexString(hash.slice(hash.length - 20), true);
addr
: sender addressepoch
: epoch when the smart contract will be deployednonce
: nonce of the deploy transaction
Example:
{
"address": "0x6f73b00d873fc21c4ca2f44bfb20653768dbd48e",
"epoch": "1",
"nonce": "2"
}
{
"address": "0x39bd250ac33f24b32af705617c0b46ead3c8f7c5"
}
Contract transaction fee
In Idena gasPrice
is calculated automatically for each block. gasPrice
is used to calculate the cost of a transaction and the cost of the gas consumed by the contract called by this transaction. The bcn_feePerGas
method should be called to get the current gasPrice
.
The total amount of fee
for the contract transaction is calculated as cost of transaction plus cost of the gas consumed by the called contract:
fee
= txCost
+ gasCost
txCost
The cost of transaction depends on its size:
txCost
= txSize
* 10 * gasPrice
So 1 byte of transaction has the same cost as 10 units of gas.
gasCost
The cost of the gas is calculated as the total amount of gas consumed by a contract multiplied to the current gasPrice
:
gasCost
= gasUsed
* gasPrice
gasUsed
is the amount of gas consumed by the contract during its execution.
maxFee
parameter
For a successful transaction, maximum transaction fee maxFee
parameter must be greater than the sum of txCost
+gasCost
.
You can use estimation methods to get an expected amount of gas that will be consumed by the contract:
Response example:
"result": {
...
"gasCost": "0.416",
"txFee": "0.56"
}
The maxFee
parameter can be omitted when calling estimation methods.
Gas limit
The max amount of gas per block is limited by 5,120,000.
The max amount of gas that can be consumed by a transaction is limited by gasLimit
, which can be calculated as follows:
gasLimit
= (maxFee
- txCost
) / gasPrice
When developing contracts, you may need to specify an explicit limit of gas for some methods. For example if another contract is being called using create_call_contract_promise
, then the gas limit has to be specified in units of wasm_gas_limit
:
wasm_gas_limit
= gasLimit
* 100
Contract transaction receipt
You can call Idena node method TxReceipt
to check the result of mined contract transaction DeployContractTx
, CallContractTx
or TerminateContractTx
Asynchronous calls
In future, storage of the Idena blockchain state will be split between shards. As a result neither the contracts data nor contracts code will not be available within the execution on a single node.
In sharded architecture the contract C1
from shard A makes an asynchronous call to shard B, which holds the state of the contract C2
. In Idena asynchronous calls for deployment, calling contracts and reading contracts data are supported. For more details about asynchronous calls see idena-sdk-as.
Unlike synchronous execution, in asynchronous calls, the result of the execution of the initial method can be successful, while the asynchronous calls end with errors. For example transaction Tx1
calls a method of the contract C1
in the Shard A. Contracts C1
creates a promise by calling another contract C2
that is executed in Shard B. If there are no errors, then the contract C1
completes its execution and commits its results to the blockchain state. To get the result of the promise, the initial contract C1
handles a callback of the contract C2
and processes possible errors.
Currently Idena node emulates asynchronous execution, while the execution takes place within a single block. However the commits to the blockchain state and the execution order of the promises correspond to the fully sharded architecture.
For the convenience of debugging and developing contracts, a hierarchical ActionResult
object is added to TxReceipt
, which contains information about execution errors, used gas, resulting data, as well as information about all the results of the created promises.