Instructions for Exchanges¶
We here describe how to integrate your exchange with the YOYOW blockchain step-by-step.
Preparation¶
Hardware¶
The recommended hardware by now is a VPS with 2GB of RAM and 20GB HDD. Single core is OK.
Supported platforms:
- Ubuntu 16.04 LTS 64 bit
- Windows Servers 64 bit
We’ll take Ubuntu as an example in this document.
Serivices¶
Disable the default time-syncing daemon (timedated) and install NTPD:
sudo timedatectl set-ntp false
sudo apt-get -y install ntp
Account¶
Register an account in https://wallet.yoyow.org. Instruction is here.
Get the private keys of your account. Instruction is here. We’ll need 3 keys for integration:
- Active key: for transferring funds out
- Secondary key: for collecting points to pay fees
- Memo key: for encrypting/decrypting memos
Business Logic¶
An exchange uses an account (or more) for processing deposits/withdrawals.
- Deposits: assign a unique identifier to every customer, such as user ID or salted hash of user ID. Every customer transfer funds to the exchange’s account with different identifiers as
MEMO
so the exchange can know a deposit is from which customer. - Withdrawals: transfer funds from the exchange’s account to an account the customer requested, with an optional memo that the customer may have requested. An optional memo is needed, because some customers may want to withdraw funds to another exchange directly.
Installation¶
Please download the “Latest Version” of the executable files (here). After downloading, extract the files. For example,
https://github.com/yoyow-org/yoyow-core/releases/download/v0.2.1-180313/yoyow-node-v0.2.1-ubuntu-20180313.tgz
https://github.com/yoyow-org/yoyow-core/releases/download/v0.2.1-180313/yoyow-client-v0.2.1-ubuntu-20180313.tgz
tar xzf yoyow-node-v0.2.1-ubuntu-20180313.tgz
tar xzf yoyow-client-v0.2.1-ubuntu-20180313.tgz
Notes: please remember to replace the latest version of node and client program.
Starting YOYOW Node¶
The node need to be always running. One way to achieve this is to run it with screen. The installation is as follow:
sudo apt-get -y install screen
Start yoyow_node with screen:
screen -S yoyow_node
./yoyow_node --rpc-endpoint 127.0.0.1:8090
Note:
- Start the node with
--rpc-endpoint
so we can interact with it via RPC call. In the example the node will listen on address127.0.0.1
and port8090
. - The following parameters indicate how many history records are kept for each account. The default value is 1000. For exchanges, if there are more recharge and withdrawal records, consider setting a larger value, e.g.:
max-ops-per-account = 1000
modify to
max-ops-per-account = 1000000
It will retain one million data. Earlier data is deleted from memory and cannot be queried quickly (but still recorded on the chain).
- The following two parameters will greatly reduce the memory required for the operation. The principle is not to save the historical data index that is not related to the exchange account.
track-account = [25638]
partial-operations = true
Please replace “25638” with the account ID you need. Note: The default track-account in config.ini has a “#” symbol and needs to be deleted.
If you need to monitor multiple accounts, use the following configuration:
track-account = [25638,25997]
partial-operations = true
The node then will download blocks from the P2P network. When it’s done (in sync), in the console there will be new messages showing every 3 seconds, like these:
789216ms th_a application.cpp:574 handle_block ] Got block: #355797 00056dd54878c05849e2dcd731c9ee364398b0d2 time: 2017-09-18T19:13:09 latency: 216 ms from: 27662/init8 irreversible: 355787 (-10)
792527ms th_a application.cpp:574 handle_block ] Got block: #355798 00056dd613f39f1ad6c0c3fdb00a56c60f44ab1c time: 2017-09-18T19:13:12 latency: 526 ms from: 499381505/yoyo499381505 irreversible: 355788 (-10)
If need to terminate the node, press Ctrl+C, or send SIGINT or SIGTERM signal to the process, then wait for a while, the node will stop by itself.
Starting YOYOW Client¶
First Run¶
Start anotherscreen
session, runyoyow_client
inside:
screen -S yoyow_client
./yoyow_client -s ws://127.0.0.1:8090/ -H 127.0.0.1:8091
Note:
- Use
-s
option to connect the client to the node. - Use
-H
option to start a HTTP-RPC service so we can interact with the client from another process, E.G. a script to process funds deposit/withdrawal. - The node won’t start listening on the RPC port until finished replaying, so please be patient in this case.
- You can connect multiple clients to one node, but don’t use the same
-H
option.
For the first time when running yoyow_client
, after connected to the node, it will show:
Please use the set_password method to initialize a new wallet before continuing
new >>>
So we need to set a password as shown below, and it will be used to create and encrypt a wallet file:
new >>> set_password my-password
set_password my-password
null
locked >>>
When there is already a wallet file, if we run yoyow_client
, it will show locked >>>
as well.
Then we need to unlock the wallet:
locked >>> unlock my-password
unlock my-password
null
unlocked >>>
Now we import the 3 private keys into the client, they will be encrypted then saved in the wallet file. The syntax is:
import_key [account_ID] [wif_private_key]
Assume your account ID is 123456789
, and you have the Active Key
, Secondary Key
and Memo Key
, we need to execute import_key
command 3 times, with one time for one key. For example:
unlocked >>> import_key 123456789 5Hqwx3xXMYZ55Pko9nzw34234234nXHcGfNQjNEL23424w7Py
import_key 123456789 5Hqwx3xXMYZ55Pko9nzw34234234nXHcGfNQjNEL23424w7Py
2993104ms th_a wallet.cpp:820 save_wallet_file ] saving wallet to file wallet.json
true
unlocked >>> import_key 123456789 5JKoYzQ4sYZoDYwreyrsfsd32466MsCFNoxRE23nExaRi6SY3
import_key 123456789 5JKoYzQ4sYZoDYwreyrsfsd32466MsCFNoxRE23nExaRi6SY3
2993104ms th_a wallet.cpp:820 save_wallet_file ] saving wallet to file wallet.json
true
unlocked >>> import_key 123456789 5HttjgBSb45368989etfhsserVtt69cWcExteq6RktpAYXNTT
import_key 123456789 5HttjgBSb45368989etfhsserVtt69cWcExteq6RktpAYXNTT
2993104ms th_a wallet.cpp:820 save_wallet_file ] saving wallet to file wallet.json
true
unlocked >>>
Command: info
¶
We can check network status with info
command:
unlocked >>> info
info
{
"head_block_num": 377867,
"head_block_id": "0005c40b41f6d79d762b1ff81c7affc7ae82a894",
"head_block_time": "2017-09-18T19:37:39",
"head_block_age": "0 second old",
"last_irreversible_block_num": 377857,
"chain_id": "3505e367fe6cde243f2a1c39bd8e58557e23271dd6cbf4b29a8dc8c44c9af8fe",
"participation": "100.00000000000000000",
"active_witnesses": [[
...
}
Command: get_block
¶
We can get the details of a specified block with get_block
command. The syntax is:
get_block [block_number]
For example:
unlocked >>> get_block 1
Command: get_full_account
¶
We can check the account info with get_full_account
command:
unlocked >>> get_full_account 123456789
get_full_account 123456789
{
"account": {
"uid": 123456789,
...
},
"statistics": {
"owner": 123456789,
"total_ops": 30220,
"prepaid": 0,
"csaf": 37424828,
"core_balance": "44672014515",
"core_leased_in": 0,
...
Note: the “statistics” data is useful for integration.
- “csaf” means “points”, which are generated by holding YOYO and will be used to pay transaction fees. Balances have 5 decimal digits in YOYOW, and the currency is YOYO, so
"csaf": 37424828
means374.24828 YOYO
. - “core_balance” is balance of the account.
"core_balance": "44672014515"
means446,720.14515 YOYO
. - Please be aware that numbers will be surrounded with quotation marks when bigger than
2^32
, as shown above forcore_balance
but not forcsaf
.
Command: transfer
¶
We can use the transfer
command to transfer funds. The syntax is:
transfer [from] [to] [amount] YOYO [memo] [broadcast]
For example:
unlocked >>> transfer 123456789 987654321 1.2345 YOYO "thisismemo" true
Note:
amount
can only have at most 5 decimal digits.- If setting
broadcast
totrue
, the signed transaction will be broadcast to the P2P network. If usingfalse
for testing, it will not be broadcast.
Command: get_transaction_id
¶
We can use the get_transaction_id
command to get the hash of a transaction. The syntax is:
get_transaction_id [transaction_in_json]
This command is useful for integration.
Command: get_relative_account_history
¶
We can use the get_relative_account_history
command to check our transaction history. The syntax is:
get_relative_account_history [account] [operation_type] [start] [limit] [end]
For example:
unlocked >>> get_relative_account_history 123456789 null 1 10 10
unlocked >>> get_relative_account_history 123456789 0 11 10 20
Note:
- For
operation_type
, usenull
to get all operations, use0
to gettransfer
only. - For
end
, use0
to get the most recent records. - Result will be in range of
[start, end]
; iflimit
is smaller than the number of records in[start, end]
, the latest records will be returned. - Result is sorted in “latest first” order.
Command: collect_csaf
¶
We can use the collect_csaf
command to collect points which will be needed to pay transaction fees. The syntax is:
collect_csaf [from_account] [to_account] [amount] YOYO [broadcast]
For example:
unlocked >>> collect_csaf 123456789 123456789 10 YOYO true
Note:
- If you have some YOYO in your account, it will accumulate points as time goes by. The accumulation speed has a linear relationship with account balance. Usually for exchanges the accumulated points should be enough to pay transaction fees.
- Points need to be collected through this command before can be used to pay transaction fees.
- Although assets in the account (balance or tipping) can be used to pay transaction fees as well in the back end, current implementation of
yoyow_client
will only try to pay fees with points (except raw-transaction signing). If you don’t have enough points in the account, most commands will fail. So it’s important to keep a certain amount of points in the account.
How to Close¶
Press Ctrl+D if the client is running in Ubuntu.
Accessing the Client via HTTP-RPC¶
When HTTP-RPC is enabled, we can access the client via HTTP-RPC call. All commands are usable. For example:
curl -d '{"jsonrpc": "2.0", "method": "info", "params": [], "id": 1}' http://127.0.0.1:8091/rpc
curl -d '{"jsonrpc": "2.0", "method": "transfer", "params": [123456789,123456789,"1","YOYO",null,true], "id": 1}' http://127.0.0.1:8091/rpc
curl -d '{"jsonrpc": "2.0", "method": "get_relative_account_history", "params": [123456789,0,1,10,10], "id": 1}' http://127.0.0.1:8091/rpc
Note:
- Use
http
as protocol - Request
/rpc
but not/
- Not like the interactive CLI, the results of HTTP-RPC calls are formatted in json
- In the request, amounts have 5 decimal digits; in the response, amounts have no decimal point. Instead, amounts are multiplied by
10^5
. - When entering the amount, it is recommended to use a string for the amount. It is recommended to use the normal decimal format, such as “12345.6789”. Problems may occur if using the scientific notation format such as 1.23456789E4.
Processing Deposits¶
Checking Node Status¶
Get the last_irreversible_block_num
data with the``info` command (via HTTP-RPC). Only blocks earlier than this block is reliable.
Checking Account History¶
- Firstly, Use
get_relative_account_history
command/API to get the latest sequence number:
curl -d '{"jsonrpc": "2.0", "method": "get_relative_account_history", "params": [123456789,0,0,1,0], "id": 1}' http://127.0.0.1:8091/rpc
If the result, response["result"]
is empty, it means we have no deposit at all. If it’s not empty, we get response["result"][0]["sequence"]
as maximum_sequence
.
If maximum_sequence
is bigger than the last sequence we’ve saved, it means there are new records to be processed.
- Use
get_relative_account_history
command/API to check for new records. For example, if last recorded sequence is 100, andmaximum_sequence
is 200, we can check from101
, for at most 100 records, to200
. Refer to the following commands:
curl -d '{"jsonrpc": "2.0", "method": "get_relative_account_history", "params": [123456789,0,101,100,200], "id": 1}' http://127.0.0.1:8091/rpc
In the returned result, result=response["result"]
, so result is an array. If the array is empty, it means no new deposit. If it’s not empty, the N’th record result[N]
should be like:
{
"memo": "a1b2c3d4",
"description": "Transfer 100 YOYO from 204501630 to 123456789 -- Memo: a1b2c3d4 (Fee: 0.20898 YOYO)",
"sequence": 101,
"op": {
"op": [
0,
{
"fee": {
"total": {
"amount": 20898,
"asset_id": 0
},
"options": {
"from_balance": {
"amount": 20898,
"asset_id": 0
}
}
},
"from": 204501630,
"to": 123456789,
"amount": {
"amount": 10000000,
"asset_id": 0
},
"memo": {
"from": "YYW6U528P71X6V87765245356aPBPpDwwRp7urUiXYtFLHmrXRsN3u",
"to": "YYW5eA89yqwerhdfghrjtr3452376trtyU6LD7a1kmvwYa5h51rDxr",
"nonce": "3457645755345345",
"message": "0938457345937abcdef3098945"
},
"extensions": {
"from_balance": {
"amount": 10000000,
"asset_id": 0
},
"to_balance": {
"amount": 10000000,
"asset_id": 0
}
}
}
],
"result": [
0,
{
}
],
"block_timestamp": "2017-09-08T11:14:15",
"block_num": 223355,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 8795
}
},
- Get
result[N]["op"]["block_num"]
, if it’s smaller thanlast_irreversible_block_num
, then it’s reliable, and need to be processed. - Get
result[N]["op"]["op"][0]
, if it’s0
, then it’s a transfer. (Although it should always be 0 if we request transfers only) - Get
result[N]["op"]["op"][1]["to"]
, if it’s the same as our account ID, then this transfer is a deposit. - Check if
result[N]["op"]["op"][1]["amount"]["asset_id"]
is0
. If yes, it means it’sYOYO
asset. - Get
result[N]["op"]["op"][1]["amount"]["amount"]
, it’s the amount. Remember to add the decimal point (5 digits). - Get
result[N]["memo"]
, it is the memo info of the transfer records. It should have been decrypted already, and it can be an identifier to the customer. - Save
result[N]["sequence"]
as the latest processed sequence number for use in the next loop. - Save
result[N]["op"]["trx_in_block"]
for later use. - Use
get_block
command/API to get this transfer’s transaction ID/Hash (change the parameter to theblock_num
):
curl -d '{"jsonrpc": "2.0", "method": "get_block", "params": [160000], "id": 1}' http://127.0.0.1:8091/rpc
From the response new_response
, save new_response["result"]["transaction_ids"][trx_in_block]
as the transaction ID/hash of this deposit for future use.
Note:
- To be able to decrypt memo, the client need to be
unlocked
, and the memo private key need to be in the wallet.
Process Withdrawal Requests¶
Check Node Status¶
To be safe, we can only process withdrawal requests when node status is normal.
Check with the info
command/API.
head_block_time
should be no more than 15 seconds oldparticipation
should be more than80
, which means 80% of block producers are online and at normal statuses.
Checking Account Balance and Points¶
Check with the get_full_account
command/API.
If points are not enough for paying transaction fee, collect more points with collect_csaf
command/API.
Sending Funds¶
- Use the
transfer
command/API to send out funds.
Note: pay attention to the decimal digits.
Save the returned json for future use.
- Get the transaction ID/hash with
get_transaction_id
command/API, save it for future use.
Re-check / Follow-up¶
Similar to the deposit processing steps, when found an outgoing transfer, save the transaction ID/hash, block number and etc. for future use.
Trouble Shooting¶
Every transaction has an expiration
field. If the transaction hasn’t been included in any block for some reason, and the timestamp of block whose number is last_irreversible_block_num
is later than the expiration
field of the transaction, the transaction won’t be included in current chain, so it’s safe to try to transfer again.
Sample Code¶
- Ruby: https://github.com/yoyow-org/yoyow-core/blob/master/scripts/exchange.rb