-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
32 changed files
with
716 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
******************************* | ||
Advanced: Multiple Transactions | ||
******************************* | ||
|
||
In this tutorial, I shall go through how can we send multiple transactions within the same block. | ||
|
||
If you have been using the current methods ``sendAsset`` and ``doInvoke``, you would have realised that the second transaction coming out from the same address will fail if done quickly enough. This is due to the second transaction being unaware of the first transaction and thus it tries to spend the same inputs as the first transaction has used. Therefore, this is double spending and the node rejects our second transaction. | ||
|
||
A look at the Balance object | ||
---------------------------- | ||
|
||
Within the Balance object, the assetBalance object looks like this:: | ||
|
||
{ | ||
balance: 1, | ||
spent: [], | ||
unspent: [ | ||
{ txid: 'abc', index: 0, value: 10 }, | ||
{ txid: 'abc', index: 1, value: 5 } | ||
], | ||
unconfirmed: [] | ||
} | ||
|
||
Other than ``balance``, each of the arrays is a list of ``Coin`` objects that basically represent a spendable transaction output. When we are constructing a transaction involving assets, ``neon-js`` selects coins from the ``unspent`` array, selecting more until there is enough to meet the demand as stated in the ``intents``. The selected coins are transformed and added to the transaction as ``inputs`` while the ``intents`` are transformed and added to the transaction as ``outputs``. | ||
|
||
Once the transaction is sent off to a RPC node, we should not reuse the selected coins as inputs for a second transaction as that would be double spending. However, if we were to retrieve a new ``Balance`` object from the 3rd party provider such as neoscan, the ``Balance`` object will not take in account the recently sent transaction. | ||
|
||
Applying Transaction | ||
-------------------- | ||
|
||
To deal with this problem, the program will have to retain the old ``Balance`` object and reuse it to make the second transaction. We register the transaction with the ``Balance`` object by calling the ``applyTx`` method:: | ||
|
||
const tx // We assume that this is a tx that sends 3 NEO away. | ||
console.log(tx.hash) // ghi | ||
balance.applyTx(tx) | ||
|
||
Now, our asset balance should look like:: | ||
|
||
{ | ||
balance: 1, | ||
spent: [{ txid: 'abc', index: 0, value: 10 }], | ||
unspent: [{ txid: 'abc', index: 1, value: 5 }], | ||
unconfirmed: [ | ||
// This is the change from spending the 5 neo | ||
{ txid: 'ghi', index: 0, value: 2} | ||
] | ||
} | ||
|
||
We can see that in order for us to send that 3 neo, we actually spent 5 neo and got back 2 neo. However, this effectively locks out 5 neo as we are unable to use that 2 neo until the transaction is confirmed. However, now we can create a new transaction without worry of double spending. Our second transaction can spend up to 10 neo. | ||
|
||
Confirming Transaction | ||
---------------------- | ||
|
||
Once the transaction is confirmed, we can always reset by grabbing a fresh ``Balance`` object by asking our 3rd party provider. But for cases where we do not want to do that, the ``confirm`` function is a simple function to move all ``unconfirmed`` coins to ``unspent``:: | ||
|
||
balance.confirm() | ||
|
||
Now, our asset balance will look like:: | ||
|
||
{ | ||
balance: 1, | ||
spent: [{ txid: 'abc', index: 0, value: 10 }], | ||
unspent: [ | ||
{ txid: 'abc',index: 0, value: 10 }, | ||
{ txid: 'ghi', index: 0, value: 2} | ||
], | ||
unconfirmed: [] | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
********************************************* | ||
Basic - Creating an invocation script (NEP5) | ||
********************************************* | ||
|
||
This is a tutorial for constructing invocation scripts for invoking smart contract functions. This tutorial is specifically targeted at NEP5 contracts as it is currently the most common contract type. | ||
|
||
The example I will be using for this tutorial can be found here_. | ||
|
||
.. _here: https://github.com/neo-project/examples-csharp/blob/master/ICO_Template/ICO_Template.cs | ||
|
||
The parts | ||
---------- | ||
|
||
We first look at the entry method for the NEP5 contract. The entry method for smart contracts are always named ``Main`` | ||
|
||
.. code-block:: C# | ||
public static Object Main(string operation, params object[] args) | ||
{ | ||
There are 2 arguments for the entry method: a string ``operation`` and an array ``args``. This means that for every invocation script, the contract expects exactly 2 arguments. | ||
Of course, this style is not enforced and you can have a smart contract that simply accepts an integer as the only argument. However, it is recommended that any complex contract implement this string and array pattern as it is super flexible and tools will most likely be built around serving this pattern. | ||
Simple calls with create.script | ||
------------------------------- | ||
In ``neon-js``, there is a simple way exposed to create an invocation script through the semantic call ``Neon.create.script``. It is an alias for the method ``Neon.sc.createScript``. | ||
:: | ||
const props = { | ||
scriptHash: '5b7074e873973a6ed3708862f219a6fbf4d1c411', // Scripthash for the contract | ||
operation: 'balanceOf', // name of operation to perform. | ||
args: [Neon.u.reverseHex('cef0c0fdcfe7838eff6ff104f9cdec2922297537')] // any optional arguments to pass in. If null, use empty array. | ||
} | ||
const script = Neon.create.script(props) | ||
``script`` is now a hexstring that you can use in a ``invokescript`` RPC call or an invocationTransaction. | ||
:: | ||
Neon.rpc.Query.invokeScript(script) | ||
.execute('http://seed3.neo.org:20332') | ||
.then(res => { | ||
console.log(res) // You should get a result with state: "HALT, BREAK" | ||
}) | ||
Your console result should look something like this:: | ||
{ | ||
"jsonrpc": "2.0", | ||
"id": 1234, | ||
"result": { | ||
"script": "143775292229eccdf904f16fff8e83e7cffdc0f0ce51c10962616c616e63654f666711c4d1f4fba619f2628870d36e3a9773e874705b", | ||
"state": "HALT, BREAK", | ||
"gas_consumed": "0.338", | ||
"stack": [ | ||
{ | ||
"type": "ByteArray", | ||
"value": "80778e06" // This value will vary | ||
} | ||
] | ||
} | ||
} | ||
The ``value`` field is our result and we can turn that into a human readable form by parsing it as a Fixed8:: | ||
Neon.u.Fixed8.fromReverseHex('80778e06') | ||
.. note:: This is a generic case for NEP5 tokens with 8 decimals. Some tokens might require different parsing solutions. This will be covered in another tutorial or you can check out the source code under src/api/nep5 . | ||
Chaining scripts | ||
---------------- | ||
Invocation transactions might be free now so we are fine with sending an transaction for every transfer but in the future, we want to aggregate them so we fully maximise the 10 free gas that we get per transaction. This is achieved by chaining scripts together. | ||
``create.script`` has the functionality in built for us. Let us use it to retrieve all the information about a specific token. | ||
There are many fields that we want to know about in an NEP5 token: name, decimals, total supply, etc. Instead of performing a ``invokescript`` RPC call for each field, we will be combining it in a single call. The example we are using here is the testnet contract for Red Pulse. | ||
:: | ||
const scriptHash = '5b7074e873973a6ed3708862f219a6fbf4d1c411' // TestNet RPX | ||
const getName = { scriptHash, operation: 'name', args: [] } | ||
const getDecimals = { scriptHash, operation: 'decimals', args: [] } | ||
const getSymbol = { scriptHash, operation: 'symbol', args: [] } | ||
const getTotalSupply = { scriptHash, operation: 'totalSupply', args: [] } | ||
const script = Neon.create.script([getName, getDecimals, getSymbol, getTotalSupply]) | ||
Similar to the previous example, our ``script`` is now ready to be used in a ``invokescript`` RPC call or an invocationTransaction. | ||
Now our result would look like:: | ||
{ | ||
"jsonrpc": "2.0", | ||
"id": 1234, | ||
"result": { | ||
"script": "00c1046e616d656711c4d1f4fba619f2628870d36e3a9773e874705b00c108646563696d616c736711c4d1f4fba619f2628870d36e3a9773e874705b00c10673796d626f6c6711c4d1f4fba619f2628870d36e3a9773e874705b00c10b746f74616c537570706c796711c4d1f4fba619f2628870d36e3a9773e874705b", | ||
"state": "HALT, BREAK", | ||
"gas_consumed": "0.646", | ||
"stack": [ | ||
{ | ||
"type": "ByteArray", | ||
"value": "5265642050756c736520546f6b656e20332e312e34" | ||
}, | ||
{ | ||
"type": "Integer", | ||
"value": "8" | ||
}, | ||
{ | ||
"type": "ByteArray", | ||
"value": "525058" | ||
}, | ||
{ | ||
"type": "ByteArray", | ||
"value": "00f871f54c710f" | ||
} | ||
] | ||
} | ||
} | ||
We can see that the result stack returns the results in the order of our scripts. The first result ``5265642050756c736520546f6b656e20332e312e34`` is the hexstring representation of the name of the token. The second result is the decimal places. The third result is the symbol in hexstring and the last result is the total supply in Fixed8 format. | ||
This last bit in parsing is intentionally left for the reader to try parsing the values themselves. | ||
:: | ||
const name = 'Red Pulse Token 3.1.4' | ||
const decimals = 8 | ||
const symbol = 'RPX' | ||
const totalSupply = 43467000.00000000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,5 @@ Tutorial | |
|
||
basic-sendasset.rst | ||
basic-claimgas.rst | ||
basic-createscript.rst | ||
adv-multitx.rst |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.