@iohk/mallet
v2.1.0
Published
IELE/KEVM testnet CLI tool
Downloads
3
Keywords
Readme
Mallet
Mallet, the minimum wallet, is a command line utility for deploying smart contracts and interacting with the Cardano IELE and KEVM testnets developed by IOHK. Mallet is written in Javascript and is based on the Node.js REPL, so it gives access to some handy functions as well as libraries such as Web3. Mallet can also be included as a library in your own JavaScript programs.
Installing
Mallet requires Node.js version 10.4.0 or later. There are more detailed instructions for installing Node.js and Mallet in two places.
First, there are pages on installing Node.js and Mallet along with a tutorial.
Second, there is a video, Getting Started with Mallet.
Once you have Node.js installed, run:
$ npm install -g --engine-strict @iohk/mallet
This will download and install all the dependencies. The --engine-strict flag makes sure you have the required version of Node installed.
Running the command line interface
Type the following to see Mallet's usage help:
$ mallet --help
Running Mallet with proper arguments will open a Node.js REPL session with the Mallet commands available.
mallet iele
Everything has to be valid JavaScript:
mallet> 1 + 1
2
mallet> const x = _
undefined
mallet> x
2
mallet> x = 3
TypeError: Assignment to constant variable.
mallet> help
[Function: help]
mallet> help()
undefined
Caveats
- Mallet is a command line tool to be run on Node.js. Although it may be possible to import Mallet into a browser, this has not been tested and is not officially supported.
- Mallet takes advantage of
BigInt
arbitrary-precision integers, which were added to the V8 Javascript engine in May 2018. - To meet the requirements of an interactive shell environment, all Mallet's functions are synchronous.
- Because of an issue with the embeddable Node.js REPL, an augmented version of rlp.js has been put in the
lib
folder
Mallet commands
Technically speaking, the ‘commands’ discussed here are actually functions and properties of a Mallet object. We refer to them as commands because that reflects how Mallet is used.
Getting help
When running Mallet, these commands may be useful.
help
Opens the README file you are reading in your default browser:
mallet> help()
undefined
listCommands
Lists the available commands:
mallet> listCommands()
[ 'currentAccount',
'getBalance',
'getNonce',
'getReceipt',
'help',
'iele.contractCall',
'iele.createContract',
'iele.simpleTransfer',
'importPrivateKey',
'lastTransaction',
'listAccounts',
'listCommands',
'newAccount',
'requestFunds',
'selectAccount',
'sendTransaction',
'web3' ]
Account management
These management commands operate on the local keystore and do not connect to a testnet.
newAccount
Creates a random key pair for a new managed account. The private key is stored in the datadir that is encrypted with the provided password. The command returns the corresponding account’s address.
mallet> newAccount()
Enter password:
Repeat password:
'0x8cc7c261b5dda47755ac9629ec32bba0ab4d1d32'
importPrivateKey
Imports a known private key as a managed account. The private key is stored in the datadir that is encrypted with the provided password. This import command returns the corresponding account’s address.
mallet> importPrivateKey()
Enter private key:
Enter password:
Repeat password:
'0xb0ff13c14e071b11ab678524ae28e6eb248de96a'
listAccounts
Lists the addresses of all managed accounts:
mallet> listAccounts()
[ '0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2',
'0xd7f3583b8805cfbe0979050f5a1b3587a8fee900',
'0x724e5991252860ca530542719b25b39b1b437a1c',
'0x60950641e7382a120c8825464391da3b84db2a86',
'0xb0ff13c14e071b11ab678524ae28e6eb248de96a' ]
selectAccount
Selects an account for commands such as sendTransaction
, getBalance
, requestFunds
:
mallet> selectAccount('0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2')
'0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2'
currentAccount
mallet> currentAccount()
'0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2'
Interacting with the testnet
The following commands interact with nodes on Mantis, the Ethereum client, via the JSON remote procedure call (RPC) protocol (using the Web3 library).
getBalance
Show the balance of an account:
mallet> currentAccount()
'0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2'
mallet> getBalance()
'98474579999999000'
mallet> getBalance('0x60950641e7382a120c8825464391da3b84db2a86')
'1000'
sendTransaction
Sends a transaction signed with the selected account’s key and returns the transaction hash. This command requires a password to decrypt the private key.
mallet> tx = {
to: '0x60950641e7382a120c8825464391da3b84db2a86', // recipient's address, optional, new contract created if not provided
gas: 100000, // gas limit, mandatory
gasPrice: 5000000000, // gasPrice, optional, default: 5 Gwei
value: 1000000, // optional, default: 0
data: '0xcafebabe' // optional, default: empty
}
mallet> sendTransaction(tx)
getReceipt
Obtains a receipt of a transaction with a given hash. If a hash is not provided, the hash of the most recent transaction will be used.
mallet> sendTransaction(tx)
Enter password:
'0xc71a634009c85640996d64124cf35c3748009c45c952ed456ff5c239ccd5b1d3'
mallet> getReceipt()
null
mallet> getReceipt()
{ transactionHash:
'0xc71a634009c85640996d64124cf35c3748009c45c952ed456ff5c239ccd5b1d3',
transactionIndex: 0,
blockNumber: 295346,
blockHash:
'0x007643f3198296fe6e1b3f125ec603aa758d813cb9ae2c7a2a06daf5357d30bc',
cumulativeGasUsed: 21272,
gasUsed: 21272,
contractAddress: null,
logs: [],
statusCode: '0x00',
status: true,
returnData: [],
rawReturnData: '0xc0' }
mallet> getReceipt('0x00039c02c1ca8cb2b25226f74887fc0afcf485797de65afbc105dab13497ba57')
{ transactionHash:
'0x00039c02c1ca8cb2b25226f74887fc0afcf485797de65afbc105dab13497ba57',
transactionIndex: 0,
blockNumber: 295367,
...
Note that the receipt may not be readily available, indicated with null
value, because it takes time for a transaction to be forged.
If the receipt is for a IELE transaction (technically if it has statusCode
field and the return data is RLP-decodable) then returnData
field contains an array of integers, and raw undecoded data (hex string) is available in rawReturnData
.
requestFunds
This command is different, in that it doesn't interact with the JSON RPC. Instead, it calls the testnet Faucet to obtain funds for a given account. It returns the transaction hash.
mallet> currentAccount()
'0xfc7e805d72ca57aff872cd010a4c9c5e8e8f22f2'
mallet> getBalance()
'972415314998966999'
mallet> requestFunds()
'0x1fc10c0ae70b09fb89fbc39d827c301d05ed0f196f3c54f3c52c4e683b89ff27'
mallet> getBalance()
'1002415314998966999'
mallet> requestFunds('0xd7f3583b8805cfbe0979050f5a1b3587a8fee900')
'0x03c485c980faf11c89d74c92a8d26efda7860edbb7de4a689220e33180a82e6c'
mallet> requestFunds('0xd7f3583b8805cfbe0979050f5a1b3587a8fee900')
Thrown: Faucet error: The user has sent too many requests in a given amount of time.
IELE commands
The following commands are variations of sendTransaction
. They ensure that data is properly encoded for the IELE virtual machine. The argument transaction object is the same as in the case of sendTransaction
, except for the data
field.
iele.simpleTransfer
This transfers value between accounts Technically, this calls the deposit
function of the IELE virtual machine on the recipient's account.
mallet> getBalance('0xd7f3583b8805cfbe0979050f5a1b3587a8fee900')
'0'
mallet> iele.simpleTransfer({to: '0xd7f3583b8805cfbe0979050f5a1b3587a8fee900', gas: 100000, value: 1000})
Enter password:
'0x1a0ef8a980851ade10f76bcb4b21cd2ad79e13d0b181419a7785ac620fbd2b82'
mallet> getBalance('0xd7f3583b8805cfbe0979050f5a1b3587a8fee900')
'1000'
iele.deployContract
Creates a contract with the bytecode provided in the code
field, with optional constructor arguments as args
– an array of integers.
mallet> let code = '00000091630369000F696E6372656D656E745828696E742969000667657458282967000000006600003400650002006180016101025511660001F60000660002620101F7016800010001660000340165000201610102541301001C6101025514660001F60000660002620102F7026800020000660000340065000200610101540A6013640001660001F6000103660002620101F701'
undefined
mallet> iele.deployContract({gas: 1000000, value: 0, code: code, args: []})
Enter password:
'0xb13e7783c86dda3f880b2d875201a1d4c4f7f0ae5b085e320dc32a7688ce394d'
mallet> getReceipt()
{ transactionHash:
'0xb13e7783c86dda3f880b2d875201a1d4c4f7f0ae5b085e320dc32a7688ce394d',
transactionIndex: 0,
blockNumber: 36577,
blockHash:
'0x4d65719d5be6ce74ea02f8a59461bde8b831ae02ad72c127e9004b29a394f4c6',
cumulativeGasUsed: 60730,
gasUsed: 60730,
contractAddress: '0x79c7f680aa944545744f611a1a9770426903cee9',
logs: [],
status: '0x00',
returnData: '0xd59479c7f680aa944545744f611a1a9770426903cee9' }
iele.callContract
Calls function func
of a contract at the to
address. As with the previous command, there are optional arguments, args
, as an array of integers.
mallet> iele.callContract({to: '0x79c7f680aa944545744f611a1a9770426903cee9', gas: 1000000, func: 'getX()', args: []})
Enter password:
'0x767f44039fbb67503a18688c38576fe0396dc55e18d057b7bef86d9a62fffb57'
mallet> getReceipt()
{ transactionHash:
'0x767f44039fbb67503a18688c38576fe0396dc55e18d057b7bef86d9a62fffb57',
transactionIndex: 0,
blockNumber: 36663,
blockHash:
'0x20d1063fad4453e15f74962ba8d9bc009b4827ca3df6b7dd130a8723f8bb6ebf',
cumulativeGasUsed: 21838,
gasUsed: 21838,
contractAddress: null,
logs: [],
statusCode: '0x00',
status: true,
returnData: [ 0n ],
returnData: '0xc100' }
mallet> iele.callContract({to: '0x79c7f680aa944545744f611a1a9770426903cee9', gas: 1000000, func: 'incrementX(int)', args: [13]})
Enter password:
'0x9592150326c811ad71e55a007b589763c6dbd6365eae2cb66f6532ca780b0799'
mallet> getReceipt()
null
mallet> getReceipt()
{ transactionHash:
'0x9592150326c811ad71e55a007b589763c6dbd6365eae2cb66f6532ca780b0799',
transactionIndex: 0,
blockNumber: 36670,
blockHash:
'0x7daf8a59044c66a1d320043287837b3d1f7d78e2b462e8dc218487cae6d80349',
cumulativeGasUsed: 28263,
gasUsed: 28263,
contractAddress: null,
logs: [],
statusCode: '0x00',
status: true,
returnData: [],
rawReturnData: '0xc0' }
iele.constantCall
This command is equivalent to web3.eth.call
except with IELE-specific data encoding. It can be used for calling contract functions that do not change the state (Solidity's view
functions). No account has to be selected to run this command. The TX argument is the same as for iele.callContract
. The command returns decoded array of integers comprising the function return data.
mallet> iele.constantCall({to: '0x79c7f680aa944545744f611a1a9770426903cee9', gas: 1000000, func: 'getX()', args: []})
[ 0n ]
Note on IELE function names
Solidity functions compiled to IELE have a naming convention that includes function the original function name along with its argument types (this is to support function overloading). Examples
| Solidity function header | IELE function name |
|--------------------------|--------------------|
| function getX() returns (int)
| getX()
|
| function incrementX(int i) |
incrementX(int)|
| function somethingComplex(address a, bytes b, int[] i) returns (string, int)
| somethingComplex(address,bytes,int[])
|
Note on type encoding
IELE functions by design accept and return array of unbounded integers. To encode/decode different Solidity to/from integers use iele.enc
/iele.dec
. Both functions take a value to be converted and a Solidity type as a string. Consider a Solidity function like this:
function dummyFunc(address a, bytes b, int[] i) public pure returns (string, int) {
return ("I'm a dummy", i[0]);
}
Here's how you can use iele.enc
and iele.dec
:
mallet> contractAddress = '0x9785367f32a97ec34090307368a4368f2ab4bc01'
'0x9785367f32a97ec34090307368a4368f2ab4bc01'
mallet> args = [iele.enc(contractAddress, 'address'), iele.enc('0xcafebabe', 'bytes'), iele.enc([42, -1], 'int[]')]
[ 865028352852446724060954038272434378925942291457n,
4222355708056220710451082685618494097063946n,
1455792646560079078679811948732730198604464062977n ]
mallet> result = iele.constantCall({to: contractAddress, gas: 1000000, func: 'dummyFunc(address,bytes,int[])', args: args})
[ 1631388912461674177904633800030782296862752779n, 42n ]
mallet> iele.dec(result[0], 'string')
'I\'m a dummy'
mallet> iele.dec(result[1], 'int')
42
Compiling contracts
Compiling contracts is currently only supported for IELE, and only for a single source file. Both IELE assembly language and Solidity (using a Solidity-to-IELE compiler) contracts can be compiled. Both compilers are services of the testnet that Mallet connects to (there are no additional dependencies).
iele.compile
Sends the source code from the provided file path to the compiler. The mode of compilation is determined by the extension of the source file:
.sol
: two-step compilation: Solidity to IELE and then to IELE assembly.iele
: direct to IELE assembly
mallet> iele.compile('test/contracts/sendEther.sol')
{ source: '// Simple contract ...'
solidityCompilerOutput: 'Warning: This is a pre-release compiler version...',
error: false,
ieleCode: 'contract "main.sol:test" ...',
bytecode: '000000AA630469000F612...' }
The compiled contract is available in the bytecode
property, which can then be used as an argument to iele.createContract
. In the case of a compilation failure, the error
property will be set to true
and the relevant compiler output can be found in solidityCompilerOutput
.
With direct IELE assembly compilation, the compiler interface is simpler:
mallet> iele.compile('test/contracts/sendEther.iele')
{ source: 'contract "sendEther" {\n\ndefine @init() ...',
result: '000000AA630469000...' }
The result
property will contain either the correctly compiled bytecode, or textual error information.
Importing as library
To use Mallet as library add:
const Mallet = require('@iohk/mallet');
in your script. See test/basic-kevm.js for an example of using Mallet in a script.
Need more help?
As mentioned before, the IELE testnet pages have detailed instructions and a rather good video:
Installing Node.js and Mallet, plus a tutorial from: https://testnet.iohkdev.io/iele/
Getting Started with Mallet video: https://www.youtube.com/watch?v=Tp4Z0RbjSa8
The Mallet repository is here: https://github.com/input-output-hk/mallet
IOHK provides support on using IELE and the testnet. Details at: https://testnet.iohkdev.io/iele/resources/support-and-help/