The Integrating with an Exchange guide explains how to integrate the BXR
token into an Exchange platform using the available SDKs. It is also possible to perform this integration using the REST API but the procedure is significantly more complicated.
This page does not aim at repeating the content of the Integrating with an Exchange guide using REST, instead, it shows how to fix the most common problems encountered when using this API in the development of an Exchange.
Feel free to jump directly to the section of your interest.
Use the /transactions/confirmed endpoint to retrieve any group of confirmed transactions. Among other filtering options, the following parameters are typically useful for narrowing down searches:
Parameter |
Type |
Effect |
---|---|---|
|
integer |
Retrieve only transactions of this type. The code for TransferTransaction is 16724 (0x4154). |
|
string |
The address receiving the transaction. Compare with This might either be a Base32 Address or a Namespace. If the bit 0 of byte 0 is not set (e.g. 0x90) then it is an address, otherwise (e.g. 0x91) it represents a namespace id which starts at byte 1. |
|
string |
The starting block height to search for. |
|
string |
The ending block height to search for. |
|
boolean |
Use Use Caution: Does not work when combined with |
Here is an example query to a NODE_URL:
NODE_URL
/transactions/confirmed
?recipientAddress=TAOXUJOTTW3W5XTBQMQEX3SQNA6MCUVGXLXR3TA
&type=16724
&fromHeight=70000
&toHeight=80000
&embedded=true
This query returns the list of all transfer transactions sent to address TAOXU...
, that happened between block height 70000 and 80000, including transactions embedded inside other transactions.
The next section explains how to parse the resulting list.
Bitxor supports Aggregate Transaction, i.e., transactions inside other transactions. For the most part it does not matter if a transaction is standalone or embedded inside another one, but there are a few differences which might be confusing when parsing incoming transactions. This section explains them.
Note
You can always receive embedded transactions, even if you never create any yourself. Make sure you understand how to parse them!
As shown in the /transactions/confirmed endpoint documentation, a successfull query returns a data array including transactions and metadata (this array is paginated so pay attention to the pageSize
and pageNumber
parameters and return values). Each one of the returned transactions can match a different schema depending on the transaction’s type, so the type
field of each transaction must be checked.
Moreover, the metadata content is also different when the transaction is embedded inside an Aggregate Transaction.
This is specially important when using the embedded=true
parameter since some of the returned transactions might be embedded transactions whereas some other might be regular transactions, and the involved schemas are different.
For example the above sample query, which filters by type=16724
(transfer transactions), actually returns both TransferTransactionDTO
and EmbeddedTransferTransactionDTO
objects because of the embedded=true
parameter. The attached metadata also varies between TransactionMetaDTO
and EmbeddedTransactionMetaDTO
.
These are the main differences to keep in mind:
The above 4 fields are missing from the embedded transaction because they belong to the containing aggregate transaction. To access them recover first the aggregate transaction using the /transactions/confirmed/{transactionId} endpoint and aggregateHash
as Id.
If you are not interested in any of the fields listed above you can safely treat regular and embedded transfer transactions the same way, since they share the rest of properties.
Tokens IDs and addresses are long random strings which are cumbersome to use. For convenience, Bitxor provides namespaces, which are user-provided text strings (aliases) that can be used instead of addresses or token IDs. A namespace can always be resolved into the actual address or token ID that it represents.
The most common example is bitxor
(Namespace ID 0xEE905A59E4F6DB7D
) which is an alias for Bitxor’s native currency (Token ID 0x3D1FE6EDC7F9611E
).
Note
Token ID 0x3D1FE6EDC7F9611E
and 0xEE905A59E4F6DB7D
can always be safely treated as equivalent.
You might find transactions using one or the other depending on whether they were created using directly the token ID or the namespace.
bitxor
is a namespace which does not expire so the above equivalence always holds. However, regular namespaces are rented for a limited amount of time, and this poses a problem when resolving them because after expiration a namespace might get rented again and be aliased to a different token or address.
Therefore, to correctly resolve a namespace found in a transaction, the block height that included the transaction must be taken into account.
This is very easy to do because all blocks which include a namespace also include either a TokenResolutionStatement or an AddressResolutionStatement containing the resolved namespace. Just use the /statements/resolutions/token and /statements/resolutions/address endpoints to retrieve all statements for a given block, and then locate the unresolved namespace ID you are interested in.
Example using TESTNET:
NODE_URL/transactions/confirmed?height=211972
retrieves all transactions included in block 211972.
"transaction": {
"size": 176,
"signature": "35DC5689...",
"signerPublicKey": "B49D1910...",
"version": 1,
"network": 152,
"type": 16724,
"maxFee": "100000",
"deadline": "8530382295",
"recipientAddress": "981D7A25D39DB76EDE6183204BEE50683CC152A6BAEF1DCC",
"tokens": [
{
"id": "E374D0B5E061EE92",
"amount": "1"
}
]
}
However, the token ID 0xE374D0B5E061EE92
does not exist (/tokens/E374D0B5E061EE92
would return a ResourceNotFound
error). Besides, the highest bit being set indicates this is actually a namespace.
You could check the current alias of this namespace by querying /namespaces/E374D0B5E061EE92
, but you actually want to know the aliased token ID at the time the transaction was confirmed.
You do this by checking the block’s TokenResolutionStatement at /statements/resolutions/token?height=211972
:
{
"statement": {
"height": "211972",
"unresolved": "E374D0B5E061EE92",
"resolutionEntries": [
{
"source": {
"primaryId": 1,
"secondaryId": 0
},
"resolved": "0DDE03C044AF95D4"
}
]
},
"id": "60DEDC83EA7C4338C56C4FB6"
}
Here you can see the resolved token ID, 0x0DDE03C044AF95D4
which is a valid ID and can be queried with /tokens/0DDE03C044AF95D4
.
Transactions are announced to the network through the /transactions endpoint which accepts an hexadecimal string representing the transaction’s payload. The process to build this payload is explained fairly extensively in the Defining a transaction guide.
The following sections aim at clarifying the points which have been deemed the most confusing by users of the API.
Transactions are not allowed to remain unconfirmed in the network forever, as this would pose a significant strain on the network’s resources. Instead, all transactions have a deadline, and are automatically disposed of when the deadline arrives.
Users are free to use any deadline they want for their transactions, between now and 6h into the future (48h for Aggregate bonded transactions). Transactions announced with a deadline outside this window will be rejected with an invalid deadline error.
Deadlines are given in milliseconds since the creation of the genesis block.
The moment when the genesis block was created can be found in the network.epochAdjustment
property of the /network/properties endpoint. This is the number of seconds elapsed since the UNIX epoch and it is always 1615853185 for MAINNET.
In other words, you need to substract the epoch adjustment from a Unix time to obtain a deadline. Therefore, a deadline 2h into the future, which is the default deadline provided by the SDK, can be calculated as:
currentTime = now(); // Seconds since the UNIX epoch
deadline = (currentTime + 7200 - epochAdjustment) * 1000;
This deadline can now be used when building the transaction, and it will expire 2h from now (7200 seconds).
The effective Fee a transaction must pay to be announced is the transaction size (in bytes) times a fee multiplier chosen by the node that confirms the transaction.
Since this multiplier is unknown when making the announcement, transactions define the maximum fee they are willing to pay.
Moreover, nodes can define a minimum fee below which transactions are just ignored.
As a consequence, choosing the right maximum fee for a transaction is very important: Too low a number and the transaction will not be confirmed by any node and will eventually expire. Too high maximum fees, though, will incur in unnecessary expenses.
To help choose the right amount, the /network/fees/transaction endpoint provides some statistics regarding the effective fees paid by the last 60 blocks. The returned data is:
Property |
Meaning |
---|---|
Highest |
Biggest fee multiplier used in the last 60 blocks. |
Median |
The median value of the fee multipliers used in the last 60 blocks. See the Dynamic fee multiplier section for more details. |
Mean |
The average value of the fee multipliers used in the last 60 blocks. |
Lowest |
Smallest fee multiplier used in the last 60 blocks. |
Min |
The minimum fee multiplier accepted by the node being queried. |
Caution
minFeeMultiplier
refers to the node being queried, whereas the rest of properties refer to the whole network.
A good rule of thumb is to use the medianFeeMultiplier
. This typically provides timely confirmation of transactions without incurring in excessive fees being paid.