Introduction
Alcor Exchange has HTTP and WebSocket api. Which can get information on various markets, orderboos, prices, events, and charts. WebSocket allows streaming for orderbook updates, new deals, and account events.
Interaction with Alcor is divided into 2 types:
- Interaction with the contract.
- Place/Cancel order.
- Add/Remove LP position.
- Obtaining information about the markets using the Alcor API services.
- Market Data
- Liquidity pools Data
- WebSocket Stream
- Bringing users' funds in from other chains.
- Bridge: deposit link, embeddable widget and transfer status
Basic documentation can be found here: docs.alcor.exchange
URL Structure
HTTP API URL are separated by chains using following structute. The UI are following same structute as api, they are splitted by subdomains named by chain.
| Chain | URL |
|---|---|
| WAX | https://wax.alcor.exchange/api/v2/ |
| EOS | https://eos.alcor.exchange/api/v2/ |
| Proton | https://proton.alcor.exchange/api/v2/ |
| Telos | https://telos.alcor.exchange/api/v2/ |
Node API
import fetch from 'node-fetch'
import { Api, JsonRpc, RpcError, JsSignatureProvider } from 'eosjs'
const rpc = new JsonRpc('https://eos.greymass.com', { fetch })
const signatureProvider = new JsSignatureProvider(['private key of order owner'])
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
Alcor is DEX, so, intereations with exchange, such as placing new order or order cancel, requires work with blockchain node api directly. Examples can be found in Trading API section.
Alcor support multiple EOSIO blockchains. Here is dex contract accounts across supported cahins:
- WAX - alcordexmain
- EOS - eostokensdex
- TELOS - eostokensdex
- Proton - alcor
You can easly interacting with alcor contracts using EOSJS library.
Alternatives:
Tokens
Token price
curl https://alcor.exchange/api/v2/tokens/tlm-alien.worlds
The above command returns JSON structured like this:
{
"contract": "alien.worlds",
"decimals": 4,
"symbol": "TLM",
"id": "tlm-alien.worlds",
"system_price": 0.252009,
"usd_price": 0.010197
}
Get single token price by id
HTTP Request
GET https://alcor.exchange/api/v2/tokens/:<token_id>
Responce
| Name | Type | Description |
|---|---|---|
| contract | string | token contract |
| decimals | number | token precision |
| symbol | string | token symbol |
| id | string | token-id |
| system_price | number | token price in chain system token |
| usd_price | number | token price in USD |
All tokens prices
curl https://alcor.exchange/api/v2/tokens
The above command returns JSON structured like this:
[
{
"contract": "alien.worlds",
"decimals": 4,
"symbol": "TLM",
"id": "tlm-alien.worlds",
"system_price": 0.252009,
"usd_price": 0.010197
},
...
]
Get all tokens with it's prices
HTTP Request
GET https://alcor.exchange/api/v2/tokens
Responce
| Name | Type | Description |
|---|---|---|
| contract | string | token contract |
| decimals | number | token precision |
| symbol | string | token symbol |
| id | string | token-id |
| system_price | number | token price in chain system token |
| usd_price | number | token price in USD |
Token Logo
curl https://wax.alcor.exchange/api/v2/tokens/wax-eosio.token/logo
Get token logo image (PNG) by token id.
HTTP Request
GET https://alcor.exchange/api/v2/tokens/<token_id>/logo
URL Parameters
| Name | Type | Description |
|---|---|---|
| token_id | string | Token identifier (symbol-contract) |
Response
Returns PNG image file if logo exists, or 404 error if not found.
Global Data
Stats
Returns global data on TVL, volumes, number of trading pairs, and so on
curl "https://alcor.exchange/api/v2/analytics/global"
The above command returns JSON structured like this:
{
"_id": "wax",
"totalValueLocked": 2240718.758763486,
"swapValueLocked": 943995.9929317994,
"spotValueLocked": 1296722.7658316866,
"swapTradingVolume": 344377.66920321307,
"spotTradingVolume": 26406.316737205285,
"swapFees": 1063.6217236406346,
"spotFees": 409.7303415365396,
"dailyActiveUsers": 4063,
"swapTransactions": 26347,
"spotTransactions": 3163,
"totalLiquidityPools": 1483,
"totalSpotPairs": 808,
"totalTradingVolume": 370783.9859404183
}
HTTP Request
GET https://alcor.exchange/api/v2/analytics/global
Query params:
| Name | Type | Description | Default |
|---|---|---|---|
| resolution | string | Accumulate results over a certain period of time | 1D |
Supported resolutions:
1D, 1W, 1M
Responce
| Name | Type | Description |
|---|---|---|
| totalValueLocked | number | Exchange TVL |
| swapValueLocked | number | Total AMM Value locked in USD |
| spotValueLocked | number | Total spot value locked in open orders |
| swapTradingVolume | number | AMM trading volume |
| spotTradingVolume | number | Spot trading volume |
| swapFees | number | AMM fees |
| spotFees | number | Spot fees |
| dailyActiveUsers | number | avg Daily active users |
| swapTransactions | number | Total AMM swaps for period(resolution) |
| spotTransactions | number | Total spot trades for period(resolution) |
| totalLiquidityPools | number | Count of AMM pairs |
| totalSpotPairs | number | Count of spot pairs |
| totalTradingVolume | number | Total trading volume for given period(resolution) |
Market Data
Token symbol repesented as SYMBOL_contract following the eosio.token standard. As the one symbol can be deployed by multiple contracts.
Trading pairs
Provides a list of all trading pairs on the Alcor DEX.
curl "https://alcor.exchange/api/v2/pairs"
The above command returns JSON structured like this:
[
{
"base": {
"id": "pgl-prospectorsw",
"contract": "prospectorsw",
"symbol": "PGL",
"precision": 4
},
"target": {
"id": "wax-eosio.token",
"contract": "eosio.token",
"symbol": "WAX",
"precision": 8
},
"ticker_id": "pgl-prospectorsw_wax-eosio.token"
}
]
HTTP Request
GET https://alcor.exchange/api/v2/pairs
Query params:
| Name | Type | Description |
|---|---|---|
| base | string | Filter pair by base currency id |
| target | string | Filter pair by target currency id |
ie: https://alcor.exchange/api/v2/pairs?base=tlm-alien.worlds
Responce
| Name | Type | Description |
|---|---|---|
| ticker_id | string | Identifier of a ticker with delimiter to separate base/target |
| base | string | Symbol code of a the base cryptoasset |
| target | string | Symbol code of the target cryptoasset |
Ticker
curl "https://alcor.exchange/api/v2/tickers/tlm-alien.worlds_wax-eosio.token"
The above command returns JSON structured like this:
{
"ticker_id": "tlm-alien.worlds_wax-eosio.token",
"market_id": 26,
"target_currency": "wax-eosio.token",
"base_currency": "tlm-alien.worlds",
"global_ticker_id": "TLM_WAX",
"target_cmc_ucid": 825,
"base_cmc_ucid": 2300
"min_buy": "0.50000000 WAX",
"min_sell": "1.0000 TLM",
"last_price": 0.32448004,
"change24": 3.42,
"high24": 0.34088342,
"low24": 0.30488872,
"bid": 0.32448004,
"ask": 0.32580358,
"base_volume": 100178.36015088,
"target_volume": 314152.7801
"frozen": false,
"fee": 20,
"base_amm_liquidity": 3710.6035,
"target_amm_liquidity": 649.92427661
}
Endpoint provides pricing and volume information on sprcific ticker.
Ticker ID represented as base token (Symbol-contract) _ quote_token (Symbol-contract) example: rda-deadcitytokn_wax-eosio.token
HTTP Request
GET https://alcor.exchange/api/v2/tickers/:ticker_id
Query Parameters
| Parameter | Mandatory | Type | Description |
|---|---|---|---|
| ticker_id | true | string | Ticker id |
Responce
| Name | Type | Description |
|---|---|---|
| ticker_id | string | Identifier of a ticker |
| global_ticker_id | string | The ticker ID according to the standard of centralized exchanges (null for unpopular tokens) |
| base_currency | string | Symbol code of a the base cryptoasset |
| target_currency | string | Symbol code of the target cryptoasset |
| target_cmc_ucid | number | CMC integration target token UCID |
| base_cmc_ucid | number | CMC integration base token UCID |
| min_buy | string | Minimum amount of target currency |
| min_sell | string | Minimum amount of base currency |
| last_price | number | Price of the latest deal |
| change24 | number | Price change in 24 hours positive/negative |
| high24 | number | Highest price in 24 hours |
| low24 | number | Lowest price in 24 |
| bid | number | Price of current highest buy order |
| ask | number | Price of current cheapest sell order |
| base_volume | number | 24H Volume of base currency |
| target_volume | number | 24H Volume of target currency |
| frozen | boolean | Trading are frozen |
| fee | number | Market fees represented as % of 1000 (fee / 1000) |
| base_amm_liquidity | number | Total base token liqiudity available on AMM contract |
| target_amm_liquidity | number | Total target token liqiudity available on AMM contract |
Tickers
curl "https://alcor.exchange/api/v2/tickers"
The above command returns JSON structured like this:
[
{
"ticker_id": "tlm-alien.worlds_wax-eosio.token",
"market_id": 26,
"target_currency": "wax-eosio.token",
"base_currency": "tlm-alien.worlds",
"min_buy": "0.50000000 WAX",
"min_sell": "1.0000 TLM",
"last_price": 0.32448004,
"change24": 3.42,
"high24": 0.34088342,
"low24": 0.30488872,
"bid": 0.32448004,
"ask": 0.32580358,
"base_volume": 100178.36015088,
"target_volume": 314152.7801
"frozen": false,
"fee": 20,
"global_ticker_id": "TLM_WAX",
"target_cmc_ucid": 2300,
"base_cmc_ucid": 9119
"base_amm_liquidity": 3710.6035,
"target_amm_liquidity": 649.92427661
}
]
Endpoint provides pricing and volume information on each market pair available on an exchange.
HTTP Request
GET https://alcor.exchange/api/v2/tickers
Without parameters returns all available tickers
Query Parameters
| Parameter | Mandatory | Type | Description |
|---|---|---|---|
| tickers | false | array | Array of ticker_id's, for getting only selected tickers |
Responce
| Name | Type | Description |
|---|---|---|
| ticker_id | string | Identifier of a ticker |
| base_currency | string | Symbol code of a the base cryptoasset |
| target_currency | string | Symbol code of the target cryptoasset |
| min_buy | string | Minimum amount of target currency |
| min_sell | string | Minimum amount of base currency |
| last_price | number | Price of the latest deal |
| change24 | number | Price change in 24 hours positive/negative |
| high24 | number | Highest price in 24 hours |
| low24 | number | Lowest price in 24 |
| bid | number | Price of current highest buy order |
| ask | number | Price of current cheapest sell order |
| base_volume | number | 24H Volume of base currency |
| target_volume | number | 24H Volume of target currency |
| frozen | boolean | Trading are frozen |
| fee | number | Market fees represented as 0.01% |
| global_ticker_id | string | The ticker ID according to the standard of centralized exchanges (null for unpopular tokens) |
| target_cmc_ucid | number | target Unified Cryptoasset ID if exist or null |
| base_cmc_ucid | numer | base Unified Cryptoasset ID if exist or null |
| base_amm_liquidity | number | Total base token liqiudity available on AMM contract |
| target_amm_liquidity | number | Total target token liqiudity available on AMM contract |
Orderbook
curl "https://alcor.exchange/api/v2/tickers/pgl-prospectorsw_wax-eosio.token/orderbook?depth=3"
The above command returns JSON structured like this:
{
"ticker_id": "PGL-prospectorsw_WAX-eosio.token",
"asks": [
[
"0.66299999", // PRICE
"111.8101" // QTY
],
[
"0.66300000",
"237.5000"
],
[
"0.66400000",
"500.0000"
],
],
"bids": [
[
"0.64800031",
"74.51997085"
],
[
"0.64800030",
"87.64158563"
],
[
"0.64598927",
"150.00000000"
],
]
}
Endpoint using to provide order book information with at least depth = 3 returned for a given ticker.
HTTP Request
GET https://alcor.exchange/api/v2/tickers/:ticker_id/orderbook
Query Parameters
| Parameter | Mandatory | Default | Description |
|---|---|---|---|
| ticker_id | true | string | ticker id |
| depth | false | 300 | The number of market depth to return on each side |
Responce
| Name | Type | Description |
|---|---|---|
| ticker_id | string | Identifier of a ticker with delimiter to separate base/target |
| bids | array | Array of bids. bid structure: [price, quantity] |
| asks | array | Array of asks. ask structure: [price, quantity] |
Latest Trades
Retrieve the most recent deals of a ticker, sorted by time from latest to oldest.
curl "https://alcor.exchange/api/v2/tickers/pgl-prospectorsw_wax-eosio.token/latest_trades?limit=2"
The above command returns JSON structured like this:
[
{
"trade_id": "61a0fd5b5f0f4674848b26ae",
"base_volume": 48.8126,
"target_volume": 29.52941386,
"price": 0.604955,
"time": 1637929769500,
"type": "buy"
},
{
"trade_id": "61a0fcf65f0f4674848b0cf4",
"base_volume": 12.9939,
"target_volume": 7.86070962,
"price": 0.60495104,
"time": 1637929668000,
"type": "sell"
}
]
HTTP Request
GET https://alcor.exchange/api/v2/tickers/:ticker_id/latest_trades
Query Parameters
| Parameter | Mandatory | Default | Description |
|---|---|---|---|
| limit | false | 300 | The number of latest trades to return |
Trade history
Retrieve the recent transactions of an instrument, sorted by time from earlyer to latest.
curl "https://alcor.exchange/api/v2/tickers/pgl-prospectorsw_wax-eosio.token/historical_trades?limit=2"
The above command returns JSON structured like this:
[
{
"base_volume": 48.8126,
"price": 0.604955,
"target_volume": 29.52941386,
"time": 1637929769500,
"trade_id": "61a0fd5b5f0f4674848b26ae",
"type": "buy"
},
{
"base_volume": 12.9939,
"price": 0.60495104,
"target_volume": 7.86070962,
"time": 1637929668000,
"trade_id": "61a0fcf65f0f4674848b0cf4",
"type": "sell"
}
]
Endpoint using to return data on historical completed trades for a given ticker.
HTTP Request
GET https://alcor.exchange/api/v2/tickers/:ticker_id/historical_trades
Sorting from lasted to oldest deals
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| type | string | Filter by "sell" or "buy" trades |
| limit | integer | Number of trades to retrieve |
| step | integer | Offset, used for step-by-step loading of the entire history |
| from | timestamp | Start time from which to query trades (milliseconds) |
| to | timestamp | End time for historical trades (milliseconds) |
Responce
| Name | Type | Description |
|---|---|---|
| trade_id | string | A unique ID associated with the trade |
| price | decimal | Transaction price. |
| base_volume | decimal | Transaction amount in base pair volume |
| target_volume | decimal | Transaction amount in target pair volume. |
| time | timestamp | Unix timestamp in milliseconds for when the transaction occurred. |
| type | string | Type of the transaction that was completed. |
Klines (Candles)
curl "https://alcor.exchange/api/v2/tickers/pgl-prospectorsw_wax-eosio.token/charts?resolution=60&from=1637856799500&to=1637869859000"
The above command returns JSON structured like this:
[
{
"close": 0.57009415,
"high": 0.58999718,
"low": 0.57009415,
"open": 0.58999718,
"time": 1637856799500,
"volume": 1.34580567 // Volume in TARGET currency
},
{
"close": 0.5889,
"high": 0.5889,
"low": 0.553,
"open": 0.57009415,
"time": 1637859602500,
"volume": 817.85241392
}
]
This endpoint retrieves klines for specific ticker in a specific range.
HTTP Request
GET https://alcor.exchange/api/v2/tickers/:ticker_id/charts
Query Parameters
from, to, resolution, limit
| Parameter | Mandatory | Type | Description |
|---|---|---|---|
| ticker_id | true | string | Ticker id |
| resolution | true | string | Resolution |
| from | false | timestamp | Start time for getting historical candles |
| to | false | timestamp | End time till which query candles |
Supported resolutions:
1, 5, 15, 30, 60, 240, 1D, 1W, 1M
Swap
Alcor is a protocol based on the AMM concept using concentrated liquidity. Allowing you to change one eosio.token to another.
And special Alcor Swap SDK for calculating everything locally, without API.
Pool
curl https://alcor.exchange/api/v2/swap/pools/0
Get pool by ID
The above command returns JSON structured like this:
{
"chain": "wax",
"id": 1095,
"active": true,
"fee": 3000,
"feeGrowthGlobalAX64": "212803631582389957186",
"feeGrowthGlobalBX64": "1171129874747599",
"feeProtocol": 3,
"liquidity": "951789470700",
"maxLiquidityPerTick": "1247497401346422",
"protocolFeeA": 10681.09414088,
"protocolFeeB": 703.6308,
"sqrtPriceX64": "45802571509710763",
"tick": -119973,
"tickSpacing": 60,
"tokenA": {
"contract": "eosio.token",
"decimals": 8,
"symbol": "WAX",
"id": "wax-eosio.token",
"quantity": 1413257.13064633
},
"tokenB": {
"contract": "usdt.alcor",
"decimals": 4,
"symbol": "USDT",
"id": "usdt-usdt.alcor",
"quantity": 81673.5964
},
"volumeA24": 60225.61186526,
"volumeAMonth": 12102207.71289808,
"volumeAWeek": 2865840.95504714,
"volumeB24": 3703.4195999999997,
"volumeBMonth": 823163.2468,
"volumeBWeek": 175806.8865,
"volumeUSD24": 7440.385309538387,
"volumeUSDMonth": 1649849.915091903,
"volumeUSDWeek": 353479.23752087017,
"tvlUSD": 168895.21809226653,
"change24": 0.3,
"changeWeek": -0.49,
"high24": 0,
"low24": 0,
"priceA": 0.0623307,
"priceB": 16.0435
}
Query params:
Pool id should be provided inside the URL structure
| Name | Type | Description | required |
|---|---|---|---|
| pool_id | number | Pool ID | true |
HTTP Request
GET https://alcor.exchange/api/v2/swap/pools/:<pool_id>
Responce
| Name | Type | Description |
|---|---|---|
| id | number | pool ID |
| active | number | is pool active |
| fee | number | fee percent as part of (fee / 10000) |
| feeGrowthGlobalAX64 | number | Global accumulated fee for token A |
| feeGrowthGlobalBX64 | number | Global accumulated fee for token B |
| feeProtocol | number | Protocol fee percent |
| liquidity | number | Pool liquidity amount |
| maxLiquidityPerTick | number | Pool max liquidity ber one tick |
| protocolFeeA | asset | Protocol earned fees |
| protocolFeeB | asset | Protocol earned fees |
| sqrtPriceX64 | number | Pool price as sqrtPriceX64 |
| tick | number | Pool current tick |
| tickSpacing | number | Pool tick spacing |
| tokenA | object | Token A |
| tokenB | object | Token B |
| volumeUSD24 | number | USD Volume for 24H |
| volumeUSDWeek | number | USD Volume for 7D |
| volumeUSDMonth | number | USD Volume for 30D |
| volumeA24 | number | 24H volume of token A |
| volumeAWeek | number | 7D volume of token A |
| volumeAMonth | number | 30D volume of token A |
| volumeB24 | number | 24H volume of token B |
| volumeBWeek | number | 7D volume of token B |
| volumeBMonth | number | 30D volume of token B |
| volumeUSD24 | number | USD Volume for 24H |
| volumeUSDWeek | number | USD Volume for 7D |
| volumeUSDMonth | number | USD Volume for 30D |
| tvlUSD | number | Total Value Locked in USD |
| change24 | number | 24H price change |
| changeWeek | number | 7D price change |
| high24 | number | 24H price high |
| low24 | number | 24H price low |
| priceA | number | token A price in terms of token B |
| priceB | number | token B price in terms of token A |
Pools
curl https://alcor.exchange/api/v2/swap/pools
Get all swap pools
The above command returns JSON structured like this:
[
{
"chain": "wax",
"id": 1095,
"active": true,
"fee": 3000,
"feeGrowthGlobalAX64": "212803631582389957186",
"feeGrowthGlobalBX64": "1171129874747599",
"feeProtocol": 3,
"liquidity": "951789470700",
"maxLiquidityPerTick": "1247497401346422",
"protocolFeeA": 10681.09414088,
"protocolFeeB": 703.6308,
"sqrtPriceX64": "45802571509710763",
"tick": -119973,
"tickSpacing": 60,
"tokenA": {
"contract": "eosio.token",
"decimals": 8,
"symbol": "WAX",
"id": "wax-eosio.token",
"quantity": 1413257.13064633
},
"tokenB": {
"contract": "usdt.alcor",
"decimals": 4,
"symbol": "USDT",
"id": "usdt-usdt.alcor",
"quantity": 81673.5964
},
"volumeA24": 60225.61186526,
"volumeAMonth": 12102207.71289808,
"volumeAWeek": 2865840.95504714,
"volumeB24": 3703.4195999999997,
"volumeBMonth": 823163.2468,
"volumeBWeek": 175806.8865,
"volumeUSD24": 7440.385309538387,
"volumeUSDMonth": 1649849.915091903,
"volumeUSDWeek": 353479.23752087017,
"tvlUSD": 168895.21809226653,
"change24": 0.3,
"changeWeek": -0.49,
"high24": 0,
"low24": 0,
"priceA": 0.0623307,
"priceB": 16.0435
}
...
]
HTTP Request
GET https://alcor.exchange/api/v2/swap/pools
Responce
| Name | Type | Description |
|---|---|---|
| id | number | pool ID |
| active | number | is pool active |
| fee | number | fee percent as part of (fee / 10000) |
| feeGrowthGlobalAX64 | number | Global accumulated fee for token A |
| feeGrowthGlobalBX64 | number | Global accumulated fee for token B |
| feeProtocol | number | Protocol fee percent |
| liquidity | number | Pool liquidity amount |
| maxLiquidityPerTick | number | Pool max liquidity ber one tick |
| protocolFeeA | asset | Protocol earned fees |
| protocolFeeB | asset | Protocol earned fees |
| sqrtPriceX64 | number | Pool price as sqrtPriceX64 |
| tick | number | Pool current tick |
| tickSpacing | number | Pool tick spacing |
| tokenA | object | Token A |
| tokenB | object | Token B |
| volumeA24 | number | 24H volume of token A |
| volumeAWeek | number | 7D volume of token A |
| volumeAMonth | number | 30D volume of token A |
| volumeB24 | number | 24H volume of token B |
| volumeBWeek | number | 7D volume of token B |
| volumeBMonth | number | 30D volume of token B |
| volumeUSD24 | number | USD Volume for 24H |
| volumeUSDWeek | number | USD Volume for 7D |
| volumeUSDMonth | number | USD Volume for 30D |
| tvlUSD | number | Total Value Locked in USD |
| change24 | number | 24H price change |
| changeWeek | number | 7D price change |
| high24 | number | 24H price high |
| low24 | number | 24H price low |
| priceA | number | token A price in terms of token B |
| priceB | number | token B price in terms of token A |
Pool Swaps
Retrieve swap transactions for a specific pool.
curl "https://alcor.exchange/api/v2/swap/pools/1095/swaps"
The above command returns JSON structured like this:
[
{
"_id": "6528d7e4eef91d4d098a39ef",
"pool": 1095,
"recipient": "jollewaxacc1",
"trx_id": "35b62eb272381b6482272f0022d5778e6154a29b334a87a1bda37251c6acbb2e",
"sender": "jollewaxacc1",
"sqrtPriceX64": "44952921109377512",
"totalUSDVolume": 0.9253599213188954,
"tokenA": 17.60908915,
"tokenB": -1.0907,
"time": "2023-05-06T08:59:51.000Z"
},
]
HTTP Request
GET https://alcor.exchange/api/v2/swap/pools/:id/swaps
Query Parameters
| Name | Type | Description | Required |
|---|---|---|---|
| from | timestamp | Start time from which to query swaps (milliseconds) | false |
| to | timestamp | End time till which to query swaps (milliseconds) | false |
| recipient | string | Account that received the swap | false |
| sender | string | Account that sent the swap | false |
| limit | integer | Number of swaps to retrieve | false |
| skip | integer | Number of swaps to skip | false |
Response
| Name | Type | Description |
|---|---|---|
| pool | number | Pool ID |
| recipient | string | Account that received the swap |
| trx_id | string | Transaction ID |
| sender | string | Account that sent the swap |
| sqrtPriceX64 | string | Sqrt price X64 |
| totalUSDVolume | number | Total USD volume |
| tokenA | object | Token A amount |
| tokenB | object | Token B amount |
| time | timestamp | Time of the swap (milliseconds) |
Pool Positions
curl https://alcor.exchange/api/v2/swap/pools/0/positions
The above command returns JSON structured like this:
[
{
"id": 0,
"owner": ".11dm.wam",
"tickLower": -443580,
"tickUpper": 443580,
"liquidity": 843101,
"feeGrowthInsideALastX64": "0",
"feeGrowthInsideBLastX64": "0",
"feesA": 0,
"feesB": 0,
"pool": 0,
"amountA": "1.6781 TLM",
"amountB": "0.42356913 WAX"
},
...
]
API for getting all positions of pool
HTTP Request
GET https://alcor.exchange/api/v2/swap/pools/<:pool_id>/positions
Query params:
Pool id should be provided inside the URL structure
| Name | Type | Description | required |
|---|---|---|---|
| pool_id | number | Pool ID | true |
Responce
| Name | Type | Description |
|---|---|---|
| id | number | position id |
| owner | string | position owner account |
| tickLower | number | lower tick of position |
| tickUpper | number | upper tick of position |
| liquidity | number | position liquidity amount |
| feeGrowthInsideALastX64 | number | token A fees grow value |
| feeGrowthInsideBLastX64 | number | token B fees grow value |
| feesA | number | token A accumulated fees |
| feesB | number | token B accumulated fees |
| pool | number | pool ID |
| amountA | asset | Positoin token A amount |
| amountB | asset | Positoin token B amount |
Output & Route calculation
curl "https://alcor.exchange/api/v2/swapRouter/getRoute?trade_type=EXACT_INPUT&input=wax-eosio.token&output=tlm-alien.worlds&amount=1.00000000&slippage=0.30&receiver=alcordexfund&maxHops=2"
The above command returns JSON structured like this:
{
"executionPrice": {
"denominator": "100000000",
"numerator": "39285"
},
"input": "1.00000000",
"maxSent": "1.00000000",
"memo": "swapexactin#0#alcordexfund#3.9167 TLM@alien.worlds#0",
"minReceived": "3.9167",
"output": "3.9285",
"priceImpact": "0.3",
"route": [
0
]
}
Finding the best route for given input/output
To calculate output amount based on input amount use trade_type EXACT_INPUT To calculate input amount based on output amount use trade_type EXACT_OUTPUT
HTTP Request
GET https://alcor.exchange/api/v2/swapRouter/getRoute
Query params:
| Name | Type | Description | required |
|---|---|---|---|
| trade_type | string | EXACT_OUTPUT or EXACT_INPUT | true |
| input | string | Input token ID | true |
| output | string | Output token ID | true |
| amount | number | Amount of input/output(depends on trade_type) | true |
| slippage | number | permissible slippage | false |
| receiver | string | Account to receive output | false |
| maxHops | number | Maximum number of intermediate pools for exchange route | false |
Responce
| Name | Type | Description |
|---|---|---|
| executionPrice | object | Execution price in format for Price object from Swap-SDK. |
| input | number | Input Amount |
| output | number | Output Amount |
| maxSent | number | Amount(with slippage included) to get exact output |
| memo | string | Memo for the transfer action |
| minReceived | number | Amount to receive with max slippage |
| priceImpact | number | Swap price impact percent |
| route | array[number] | Sequence of the pools id's that swap will use |
Account
Account data api
Account Spot Deals
curl https://alcor.exchange/api/v2/account/<account>/deals
The above command returns JSON structured like this:
[
{
"_id": "6675d517cbc8490fd50823ab",
"market": 763,
"type": "buymatch",
"trx_id": "14169e8d274786b984917c79f14cefa4e13d2dc823604d47f88a11c116d9e901",
"unit_price": 0.0410099,
"ask": 2.75797508,
"asker": "foreverstone",
"bid": 0.1131,
"bidder": "alcordexfund",
"time": "2024-06-21T19:31:33.000Z"
},
...
]
API for getting the spot deals history of a specific account.
HTTP Request
GET https://alcor.exchange/api/v2/account/<account>/deals
Query params:
| Name | Type | Description | Required |
|---|---|---|---|
| account | string | Account ID | true |
| from | number | Start time in seconds | false |
| to | number | End time in seconds | false |
| limit | number | Limit of records to return | false |
| skip | number | Number of records to skip | false |
| market | number | Market ID | false |
Response
| Name | Type | Description |
|---|---|---|
| _id | string | Deal ID |
| market | number | Market ID |
| type | string | Type of the deal |
| trx_id | string | Transaction ID |
| unit_price | number | Unit price |
| ask | number | Ask amount |
| asker | string | Asker account |
| bid | number | Bid amount |
| bidder | string | Bidder account |
| time | string | Time of the deal |
Account Positions
curl https://alcor.exchange/api/v2/account/<account>/positions
The above command returns JSON structured like this:
[
{
"id": 13095,
"owner": "alcordexfund",
"tickLower": 40140,
"tickUpper": 65520,
"liquidity": "1009363631498",
"feeGrowthInsideALastX64": "0",
"feeGrowthInsideBLastX64": "0",
"feesA": "41789.8580 BRWL",
"feesB": "1873.58385095 WAX",
"pool": 667,
"depositedUSDTotal": 9782.8141,
"closed": false,
"collectedFees": {
"tokenA": 0,
"tokenB": 0,
"inUSD": 0
},
"inRange": false,
"amountA": "0.0000 BRWL",
"amountB": "192032.76838783 WAX",
"totalValue": 7755.19,
"pNl": -2027.6241
},
...
]
API for getting all positions belong to specific account
HTTP Request
GET https://alcor.exchange/api/v2/swap/pools/<:pool_id>/positions
Query params:
Pool id should be provided inside the URL structure
| Name | Type | Description | required |
|---|---|---|---|
| pool_id | number | Pool ID | true |
Responce
| Name | Type | Description |
|---|---|---|
| id | number | position id |
| owner | string | position owner account |
| tickLower | number | lower tick of position |
| tickUpper | number | upper tick of position |
| liquidity | number | position liquidity amount |
| feeGrowthInsideALastX64 | number | token A fees grow value |
| feeGrowthInsideBLastX64 | number | token B fees grow value |
| feesA | number | token A accumulated fees |
| feesB | number | token B accumulated fees |
| pool | number | pool ID |
| amountA | asset | Positoin token A amount |
| amountB | asset | Positoin token B amount |
depositedUSDTotal | number | total USD value deposited to position closed | number | is position closed collectedFees | object | Fees collected by position inRange | Boolean | is position in range totalValue | number | Total position value in USD pNl | number | Profit & Loss (totalValue - depositedUSDTotal)
Account Positions History
curl https://alcor.exchange/api/v2/account/<account>/positions-history
The above command returns JSON structured like this:
[
{
"_id": "666fe1a9248953a275e29568",
"tokenAUSDPrice": 0.04254746,
"tokenBUSDPrice": 0.622996928304,
"owner": "alcordexfund",
"type": "collect",
"id": 43827,
"pool": 1259,
"tokenA": 740.63011288,
"tokenB": 39.0449,
"totalUSDValue": 55.8368,
"trx_id": "cee97277979cee5ffcfa7df078050ecce9026e87b7b3eced7e517e6b10c9348c",
"time": "2024-06-17T07:11:35.000Z"
},
...
]
API for getting the position history of a specific account.
HTTP Request
GET https://alcor.exchange/api/v2/account/<account>/positions-history
Query params:
| Name | Type | Description | Required |
|---|---|---|---|
| account | string | Account ID | true |
| limit | number | Limit of records to return | false |
| skip | number | Number of records to skip | false |
Response
| Name | Type | Description |
|---|---|---|
| _id | string | Position history ID |
| tokenAUSDPrice | number | Token A USD price |
| tokenBUSDPrice | number | Token B USD price |
| owner | string | Position owner account |
| type | string | Type of the position history |
| id | number | Position history ID |
| pool | number | Pool ID |
| tokenA | number | Token A amount |
| tokenB | number | Token B amount |
| totalUSDValue | number | Total USD value |
| trx_id | string | Transaction ID |
| time | string | Time of the position history |
Account Swap History
curl https://alcor.exchange/api/v2/account/<account>/swap-history
The above command returns JSON structured like this:
[
{
"_id": "6670108cebb327bcc470ffc3",
"pool": 1166,
"trx_id": "e9815f8ca7af5d56d08a0163ec98f1142db097ee0d356b49cb3fb2182d596006",
"sender": "alcordexfund",
"sqrtPriceX64": "335692961505657228690",
"totalUSDVolume": 20.3636278111581,
"tokenA": 14027.5692,
"tokenB": -480.37435682,
"time": "2024-06-17T10:31:37.500Z"
},
...
]
API for getting the swap history of a specific account.
HTTP Request
GET https://alcor.exchange/api/v2/account/<account>/swap-history
Query params:
| Name | Type | Description | Required |
|---|---|---|---|
| account | string | Account ID | true |
| limit | number | Limit of records to return | false |
| skip | number | Number of records to skip | false |
Response
| Name | Type | Description |
|---|---|---|
| _id | string | Swap history ID |
| pool | number | Pool ID |
| trx_id | string | Transaction ID |
| sender | string | Swap sender account |
| sqrtPriceX64 | string | Square root price X64 |
| totalUSDVolume | number | Total USD volume |
| tokenA | number | Token A amount |
| tokenB | number | Token B amount |
| time | string | Time of the swap history |
OnChain Data
To fetch data (orders/markets) directly from blockchain you have to use NodeAPI
Contract tables structute can be found here https://docs.alcor.exchange
Markets
cleos -u https://wax.greymass.com get table alcordexmain alcordexmain markets
import fetch from 'node-fetch'
import { Api, JsonRpc, RpcError } from 'eosjs'
const rpc = new JsonRpc('https://wax.greymass.com', { fetch })
// Get markets
const { rows } = await rpc.get_table_rows({
code: 'alcordexmain', // dex account
table: 'buyorder', // side
limit: 1000,
scope: 'alcordexmain', // scope same as contract name
})
The above command returns JSON structured like this:
{
"rows": [{
"id": 0,
"base_token": {
"sym": "8,WAX",
"contract": "eosio.token"
},
"quote_token": {
"sym": "4,PGL",
"contract": "prospectorsw"
},
"min_buy": "0.00000100 WAX",
"min_sell": "0.0001 PGL",
"frozen": 0,
"fee": 0
}, ...]
}
Markets are stored in markets table, scoped by contract name.
market_id are used as scope for orders table and should be provided as parameter for canceling order.
Table structute
| key | description |
|---|---|
| id | market_id |
| base_token | target currency |
| quote_token | base_token currency |
| min_buy | Min buy amount |
| min_sell | Min sell amount |
| frozen | Market are frozen if 1 |
| fee | Market fee |
Orders
cleos -u https://wax.greymass.com get table alcordexmain 26 buyorder -l 2
import fetch from 'node-fetch'
import { Api, JsonRpc, RpcError } from 'eosjs'
const rpc = new JsonRpc('https://wax.greymass.com', { fetch })
// Get buy orderbook from conract table
const { rows } = await rpc.get_table_rows({
code: 'alcordexmain',
table: 'buyorder',
limit: 1000, // limit up to 1000
scope: 26, // Market id from /api/markets or markets table
key_type: 'i128', // we are using it for getting order sorted by price.
index_position: 2
})
The above command returns JSON structured like this:
{
"rows": [{
"id": 28,
"account": "pxawpxawpxaw",
"bid": "10.00000000 WAX",
"ask": "1999.9560 TLM",
"unit_price": "500012",
"timestamp": 1603653505
},{
"id": 276,
"account": "e43am.waa",
"bid": "35.00000000 WAX",
"ask": "5000.0000 TLM",
"unit_price": "700000",
"timestamp": 1609173873
}
],
"more": true,
"next_key": "558"
}
Orders are stored in buyorder and sellorder tables. Scoped by market_id(you can find one in markets table)
Table structute
| key | description |
|---|---|
| id | order id |
| account | order owner account |
| unit_price | Price in integer |
| timestamp | Order time creation |
WebSocket
import { io } from 'socket.io-client'
const socket = io('https://alcor.exchange')
Alcor using Socket.IO for interact via WebSocket technology.
There are two commands for subscribing to and channel with specific information.
- subscribe: Subscribing for an specific room.
- unsubscribe: Unsubscribe from sprcific room.
You can subscribe to rooms to receive specific event information.
- deals
- ticker
- account
- orderbook
Deals
// Subscribe to deals
socket.emit('subscribe', {
room: 'deals',
params: {
chain: 'wax', // Chain
market: 26 // Market id
}
})
// Handle new deals
socket.on('new_deals', deals => { ... })
// Unsubscribe from deals (will unsubscribe from all markets)
socket.emit('unsubscribe', { room: 'deals', params: { chain: 'wax' } })
Responce:
[
{
"time": 1674820074000,
"ask": 3.5771,
"bid": 0.99799129,
"type": "buymatch",
"unit_price": 0.27899985,
"trx_id": "d4acaa5ae75bccad12ca82dd2d1d3fa5822cece1307ced10ee0daefaeb6d5009"
}
]
Subscribing to new deals for specific market.
Room name: deals
Params:
| Key | Value |
|---|---|
| chain | chain id |
| market | market id |
Orderbook
// Subscribe to buy orderbook of 26 (TLM on wax) market.
socket.emit('subscribe', {
room: 'deals',
params: {
chain: 'orderbook', // Chain
market: 26, // Market id,
side: 'buy'
}
})
// On orderbook bid side update
socket.on('orderbook_buy', bids => { ... })
// On orderbook ask side update
socket.on('orderbook_sell', asks => { ... })
// Unsubscribe from orderbook (all, buy and sell)
socket.emit('unsubscribe', { room: 'orderbook', params: { chain: 'wax', market: 26 } })
Responce:
[
[
27899985,
3044158,
8493196253
]
]
Subscribe to orderbook updates. First update is full order book state.
Room name: orderbook
Params:
| Key | Value |
|---|---|
| chain | chain id |
| market | market id |
| side | sell or buy |
Ticker
// Subscribe
socket.emit('subscribe', {
room: 'ticker',
params: {
chain: 'wax',
market: 26,
resolution: '30'
}
})
// Unsubscribe
socket.emit('unsubscribe', {
room: 'ticker',
params: {
chain: 'wax',
market: 26,
resolution: '30'
}
})
Responce:
[
"tick",
{
"close": 0.01175084,
"open": 0.01175084,
"high": 0.01175084,
"low": 0.01175084,
"volume": 0.11727339,
"time": 1674821301500
}
]
Subscribe for chart updates.
Room name: ticker
Params:
| Key | Value |
|---|---|
| chain | chain id |
| market | market id |
| resolution | Resolution (see list below) |
Supported resolutions:
1, 5, 15, 30, 60, 240, 1D, 1W, 1M
Account
socket.emit('subscribe', {
room: 'account', params: {
chain: 'wax',
'name': 'randomuser' // Account for listen events
}
})
Responce
[
"match",
{
"ask": 0.11727339,
"market_id": 156,
"price": 0.01175084
}
]
Subscribe to account events/notifications. Only order matches for now.
Room name: account
Params:
| Key | Value |
|---|---|
| chain | chain id |
| name | Account |
Trade API
To trade assets you sould use Blockchain API directly. More information on Interaction/NodeAPI.
Place order
cleos push action tktoken transfer \
'[bob, alcordexmain, "0.5000 TKT", "0.0010 EOS@eosio.token"]' -p bob
const actions = [{
account: 'tktoken', // token contract
name: 'transfer',
authorization: [{
actor: 'useraaaaaaaa', // account placing order (owner)
permission: 'active',
}],
data: {
from: 'useraaaaaaaa',
to: 'alcordexmain',
quantity: '0.5000 TKT', // Bid
memo: '0.0001 EOS@eosio.token' // Ask
}
}]
// Result of transaction
const r = await api.transact(actions)
Bob are selling 0.5 TKT and buying 0.001 EOS.
Send the amount(bid) you want to sell to dex contract account, and specify the amount you ask in the memo, the price and market will be automatically determined in the contract.
Memo format(ask token):
<token_amount> <token_symbol>@<token_contract>
Place multiple orders
Same way as described but transaction may contain multiple actions(array).
Cancel order
cleos push action alcordexmain cancelsell \
'[bob, 262, 1284277]' -p bob
const result = await api.transact([
{
account: 'tktoken',
name: 'transfer',
authorization: [{
actor: 'useraccountname',
permission: 'active',
}],
data: {
from: 'useraccountname',
to: 'alcordexmain',
quantity: '0.5000 TKT',
memo: '0.0010 EOS@eosio.token'
}
}
]);
Bob are canceling sell order on 262 market.
Call action cancelsell or cancelbuy with parameters:
- executor - order owner account name
- market_id - id of the order related market
- order_id - order id.
CPU Payer (WAX only)
Alcor provides a free CPU payer service for WAX blockchain that covers transaction CPU costs for users interacting with Alcor contracts. This uses the ONLY_BILL_FIRST_AUTHORIZER mechanism.
How it works
- Client checks
/statusendpoint to see if service is available - If available, client builds transaction with
liquid.alcor::noopas first action - Client signs the transaction (only their actions, noop has payer's auth)
- Client sends serialized transaction to
/cosignendpoint - Worker validates and cosigns for
liquid.alcor@bw - Client combines signatures and pushes to blockchain
- CPU cost is billed to
liquid.alcor(first authorizer)
Status
curl -X POST https://wax.alcor.exchange/api/v2/cpu/status \
-H "Content-Type: application/json"
The above command returns JSON structured like this:
{
"available": true,
"signing": true,
"throttled": false,
"cpu": {
"used": 100000,
"max": 400000,
"percent": 25.0
},
"lastCheck": 1705123456789
}
Check if CPU payer service is available and signing.
HTTP Request
POST https://wax.alcor.exchange/api/v2/cpu/status
Response
| Name | Type | Description |
|---|---|---|
| available | boolean | Whether the service is available |
| signing | boolean | Whether the service is currently signing transactions |
| throttled | boolean | Whether rate limiting is in effect |
| cpu | object | Current CPU usage info |
| cpu.used | number | Current CPU usage in microseconds |
| cpu.max | number | Maximum CPU available in microseconds |
| cpu.percent | number | CPU usage percentage |
| lastCheck | number | Timestamp of last CPU status check |
Cosign
curl -X POST https://wax.alcor.exchange/api/v2/cpu/cosign \
-H "Content-Type: application/json" \
-d '{"serializedTransaction": "..."}'
The above command returns JSON structured like this:
{
"signature": "SIG_K1_..."
}
Cosign a pre-built transaction. The transaction must have liquid.alcor::noop as the first action with liquid.alcor@bw authorization.
HTTP Request
POST https://wax.alcor.exchange/api/v2/cpu/cosign
Request Body
| Name | Type | Description | Required |
|---|---|---|---|
| serializedTransaction | string | Hex-encoded serialized transaction | true |
Response
| Name | Type | Description |
|---|---|---|
| signature | string | The payer's signature (SIG_K1_...) |
Errors
{
"error": "Rate limit exceeded"
}
| Error | Description |
|---|---|
| Rate limit exceeded | Too many requests from this IP or account |
| Invalid transaction | Transaction format is invalid |
| First action must be noop | Transaction doesn't start with liquid.alcor::noop |
| Unauthorized contract | Transaction contains actions to non-whitelisted contracts |
| Service unavailable | CPU payer is currently disabled or overloaded |
Rate Limits
| Limit | Default | Description |
|---|---|---|
| Per IP (hourly) | 500 | Requests per IP per hour |
| Per Account (hourly) | 300 | Requests per WAX account per hour |
| Per IP (daily) | 5000 | Requests per IP per day |
| Per Account (daily) | 2000 | Requests per WAX account per day |
| Global (hourly) | 10000 | Total requests per hour |
| Global (daily) | 80000 | Total requests per day |
Allowed Contracts
Transactions can only contain actions to whitelisted Alcor contracts:
alcordexmain- Spot DEXswap.alcor- AMM Swapotc.alcor- OTC tradingalcorotcswap- OTC swapliquid.alcor- CPU payer contract
Any transfer actions where the recipient (to) is one of the Alcor contracts above are also allowed.
Bridge
The Alcor bridge moves tokens between Ethereum, BNB Smart Chain, Telos and WAX. For partners the interesting route is Ethereum → WAX. The user signs one transaction on Ethereum and the bridge delivers the tokens to their WAX account by itself: there is nothing to claim and no WAX wallet involved. Small deposits usually arrive in 13–20 minutes. The live estimate is in Chains.
There are four ways to integrate it:
- Deposit link: send users to the Alcor bridge page with the transfer prefilled.
- Deposit widget: open the bridge over your own page in a modal, popup or inline block.
- Bridge API: read routes, limits, timing and transfer status yourself.
- Bridge contracts: call the contracts from your own code or bot, with no Alcor page involved.
Deposit link
https://alcor.exchange/v/wax/bridge?from=ethereum&to=wax&recipient=alice.wam&token=USDC&amount=50&partner=mygame&return_url=https://mygame.io/play
A plain URL to the bridge page with the transfer already filled in. No code is needed on your side.
Query Parameters
| Parameter | Example | Description |
|---|---|---|
| from | ethereum |
Chain to start on. The user can still change it. |
| to | wax |
Chain the tokens land on. |
| recipient | alice.wam |
Receiving account, prefilled. The user can edit it on this page. |
| token | USDC |
Asset, by the symbol the bridge lists (USDC, USDT, ETH). |
| amount | 50 |
Prefilled amount, in token units. |
| partner | mygame |
Your id, so the deposits you bring are attributed to you. |
| return_url | https://mygame.io/play |
Shows a "← Back to mygame.io" link. https only (http is allowed on localhost). |
All parameters are optional. A malformed value is ignored rather than guessed at.
Deposit widget
// <script src="https://alcor.exchange/sdk/bridge/v1.js"></script>
document.getElementById('deposit').onclick = function () {
AlcorBridge.open({
to: 'wax',
recipient: 'alice.wam', // fixed: the user cannot change it
token: 'USDC',
amount: '50',
partner: 'mygame',
onSent: function (e) { console.log('on its way', e.transactions) },
onFinished: function (e) {
if (e.outcome === 'delivered') refreshBalance()
},
onClose: function () {},
})
}
# Without the script: embed the widget yourself
<iframe src="https://alcor.exchange/embed/bridge?to=wax&recipient=alice.wam&token=USDC&partner=mygame&mode=inline"
style="width:100%;max-width:480px;height:640px;border:0"></iframe>
Load https://alcor.exchange/sdk/bridge/v1.js and call one of its functions.
The script is tiny and has no dependencies. The path is versioned:
v1.js keeps its options and events for as long as anyone uses it.
Functions
| Function | Shows | Returns |
|---|---|---|
AlcorBridge.open(options) |
Modal over your page | { close() } |
AlcorBridge.popup(options) |
Separate window. Call it from a click handler. | { close() }, or null if the popup was blocked |
AlcorBridge.mount(element, options) |
Inline iframe that grows with its content | { destroy() } |
AlcorBridge.url(options) |
Nothing. Returns the widget address for your own iframe or link. | string |
Browser extension wallets (MetaMask, Rabby) and WalletConnect work in all modes. In the popup the user's existing Alcor session and wallet connection carry over.
Options
| Option | Required | Description |
|---|---|---|
| to | true | Chain the tokens land on: wax, telos or ethereum. Into WAX the bridge pays out by itself. A deposit to Telos ends with a Collect step signed on Telos, and a withdrawal to Ethereum with a Release step signed on Ethereum. The widget shows that button when its turn comes. |
| recipient | false | Receiving account. When given, it is fixed in the widget. For WAX, the widget checks that the account exists before anything is signed. |
| from | false | Chain to start on. Defaults to ethereum, or telos when to is Ethereum. |
| token | false | USDC, USDT, ETH. |
| amount | false | Prefilled amount, in token units. |
| partner | false | Your id, for attribution. |
| origin | false | Where the widget is served from. Defaults to the origin of the script itself. |
| onReady, onSent, onFailed, onFinished, onClose | false | Event callbacks, see below. |
Events
onSentreceives:
{
"direction": "hop",
"from_chain": "ethereum",
"to_chain": "wax",
"token": "USDC",
"amount": "50",
"sender": "0x285202c8db763db06ab42e0705104cb7919ef95e",
"recipient": "alice.wam",
"partner": "mygame",
"embed": "iframe",
"transactions": [
{ "chain": "Ethereum", "label": "Send", "ref": "0x0af8…2917", "url": "https://etherscan.io/tx/0x0af8…2917" }
]
}
onFinishedreceives:
{
"key": "d:1:12",
"outcome": "delivered",
"token": "USDC",
"amount": "50",
"transactions": [ … ]
}
| Callback | When | Data |
|---|---|---|
| onReady | The widget has loaded and is prefilled | { to, recipient } |
| onSent | The user signed and the transfer is on its way | Transfer details and the first transaction |
| onFailed | The transfer could not be started, e.g. rejected in the wallet | Transfer details plus errorCode, error |
| onFinished | The transfer reached its end | { key, outcome, token, amount, transactions } |
| onClose | The user closed the widget | {} |
outcome is delivered, or returned if the destination refused the tokens
and they went back to the sender. key identifies the transfer in
Transfer status.
Messages
If you embed the iframe yourself, listen for postMessage events from
https://alcor.exchange:
{ source: "alcor-bridge", version: 1, type, data }
| Type | Data |
|---|---|
| ready, sent, failed, finished, close | Same as the callbacks above |
| resize | { height }, the content height in pixels, so you can size the iframe |
Add mode=inline to the widget URL to hide its close button. Everything in the
messages is public on-chain data.
Bridge API
Read-only HTTP API of the bridge indexer, open to any origin (CORS *).
https://telos.alcor.exchange/api/bridge/v1/
Chains are named by id: mainnet (Ethereum), bsc (BNB Smart Chain), telos-production (Telos), wax (WAX).
Amounts are integers in the asset's smallest unit. Divide by 10^precision.
Routes
curl https://telos.alcor.exchange/api/bridge/v1/routes
The above command returns JSON structured like this:
{
"routes": [
{
"symbol": "USDC",
"precision": 6,
"from": "mainnet",
"to": "wax",
"via": "telos-production",
"open": true,
"min": "5000000",
"max": "98735000000",
"how": {
"deposit": {
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"telosTo": "hop.alcor",
"memo": "1181148696416462999:<account>|1:<your address>"
}
}
}
]
}
Every way each asset can travel right now, with its limits.
HTTP Request
GET https://telos.alcor.exchange/api/bridge/v1/routes
Response
| Name | Type | Description |
|---|---|---|
| symbol | string | Asset symbol |
| precision | number | Decimals of min and max |
| from, to | string | Chain ids of both ends |
| via | string | Chain the transfer passes through, or null for a direct route |
| open | boolean | Whether the route accepts transfers now |
| min, max | string | Amount limits, in the smallest unit |
| how | object | How the transfer is started. Used by the widget, and by your own code if you send the transaction yourself. |
Chains
curl https://telos.alcor.exchange/api/bridge/v1/chains
Relevant part of the response:
{
"sources": [
{
"id": "mainnet",
"name": "Ethereum",
"chainId": 1,
"timing": {
"deposit": {
"seconds": 990,
"range": [768, 1212],
"measured": true,
"human": "about 17 min (13 min–20 min)"
}
}
}
]
}
State of each chain the bridge reaches, including how long a transfer from it currently takes. Timing is measured on recent transfers, not configured.
HTTP Request
GET https://telos.alcor.exchange/api/bridge/v1/chains
Response
| Name | Type | Description |
|---|---|---|
| sources[].id | string | Chain id |
| sources[].name | string | Display name |
| sources[].timing.deposit.seconds | number | Typical time for a deposit from this chain |
| sources[].timing.deposit.range | number[] | Fastest and slowest recent times, in seconds |
| sources[].timing.deposit.measured | boolean | true when measured on recent transfers |
| sources[].timing.deposit.human | string | The same, phrased for people |
Transfer status
curl https://telos.alcor.exchange/api/bridge/v1/hops/d:1:12
curl "https://telos.alcor.exchange/api/bridge/v1/hops?party=alice.wam"
The above command returns JSON structured like this:
{
"key": "d:1:12",
"status": "delivered",
"progress": {
"stage": "delivered on WAX",
"percent": 100,
"etaSeconds": null
},
"from": { "chain": "mainnet", "address": "0x285202c8db763db06ab42e0705104cb7919ef95e" },
"to": { "chain": "wax", "recipient": "alice.wam" },
"back": { "chain": "mainnet", "recipient": "0x285202c8db763db06ab42e0705104cb7919ef95e" },
"deposit": {
"symbol": "USDC",
"amount": "50000000",
"precision": 6,
"src_tx": "0x0af8049909488c9ebb129dbc30db39ca0c7dc7009978756a8562e38970f92917"
}
}
A transfer from Ethereum to WAX, by its key (the key from the widget's
onFinished), or every transfer an account or address took part in. The list
form returns { "hops": [ … ] }, newest first.
HTTP Request
GET https://telos.alcor.exchange/api/bridge/v1/hops/<key>
GET https://telos.alcor.exchange/api/bridge/v1/hops?party=<account or address>
Status
| Status | Meaning |
|---|---|
| arriving | Locked on Ethereum, waiting for Ethereum to finalize |
| sending | Passed through Telos, on its way to the destination |
| delivered | Paid out to the recipient |
| bounced | Refused by the destination, sent back |
| returning | Coming back to the sender |
| returned | Back with the sender |
Bridge contracts
Everything the widget does is a public contract call. Bots, wallets and exchanges can make the same calls themselves: no API key, no allowlist, no Alcor page involved.
How the bridge is built:
- Telos holds the ledger,
bridge.alcor. Every route runs between Telos and one other chain. Ethereum ↔ WAX is two legs, joined by thehop.alcorcontract on Telos. - A deposit is locked in the vault on its chain, proved on Telos and credited there. The bridge's relayer pushes the proof, so you send one transaction.
- A withdrawal is burned on Telos and proved on the destination chain. Into WAX the relayer pays it out. Into Ethereum or BSC you send the proof yourself, in one transaction (see Release on Ethereum or BSC).
- Keys name each leg:
d:<domain>:<nonce>for a deposit,w:<id>for a withdrawal. Every endpoint of the Bridge API takes them.
Addresses
| Chain | Contract | Address | What it is |
|---|---|---|---|
| Ethereum | AlcorVault |
0x3e447d533321ad6a8412f97034ac295a9ff8d858 |
Deposits in, withdrawals paid out |
| Ethereum | AlcorWithdrawals |
0x04fb700b93eb68cadd88d8e8d4dfe4857d5cb3c6 |
Verifies a withdrawal proof and tells the vault to pay |
| Ethereum | USDC, USDT | 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0xdac17f958d2ee523a2206206994597c13d831ec7 |
ETH is native: token 0x0000000000000000000000000000000000000000 |
| BSC | AlcorVault |
0x53f18eaa8bf8099b5ba21bb7e11ed311b677690e |
Same contract as on Ethereum |
| BSC | AlcorWithdrawals |
0x6132b4d02e02f1b378f0fc67c3d4f553eb76acb5 |
Same contract as on Ethereum. BNB is native |
| Telos | bridge.alcor |
The ledger: credits deposits, opens withdrawals | |
| Telos | wrap.alcor |
Bridged tokens on Telos: USDC and USDT (6 decimals), ETH, BNB and WAX (8) |
|
| Telos | hop.alcor |
Carries a transfer between two chains through Telos | |
| WAX | bridge.alcor |
The vault on WAX: holds WAX, mints and burns the mirrors | |
| WAX | wrap.alcor |
Mirror tokens on WAX: USDC, USDT, ETH, BNB. WAX itself is eosio.token |
ABIs of the EVM contracts:
AlcorVault.json,
AlcorWithdrawals.json.
The Antelope contracts publish theirs on chain (get_abi).
Domains and units
| Chain | Domain |
|---|---|
| Ethereum | 1 |
| BSC | 56 |
| WAX | 1181148696416462999 (first 8 bytes of the WAX chain id) |
The domain names the far chain in every memo and key. It is a decimal string in the API: the WAX one is larger than a JSON number holds exactly.
Amounts. The ledger counts in Telos units (the token's precision on
Telos). On an EVM chain, amount = canonical × scale: scale is 1 for USDC
and USDT and 10^10 for ETH and BNB (18 decimals there, 8 on Telos). A
remainder below scale is not taken: an ERC-20 deposit pulls only
canonical × scale, and the excess of a native deposit is sent back in the
same transaction.
Telos accounts on EVM. telosTo is an Antelope account name packed into a
uint64, the standard Antelope encoding. With
wharfkit: BigInt(Name.from('hop.alcor').value.toString()).
Deposit from Ethereum or BSC
import { Contract, toUtf8Bytes, hexlify } from 'ethers' // ethers v6
import { Name } from '@wharfkit/antelope'
const API = 'https://telos.alcor.exchange/api/bridge/v1'
const vaultAbi = await (await fetch('https://api.alcor.exchange/abi/AlcorVault.json')).json()
const { sources } = await (await fetch(`${API}/chains`)).json()
const vault = new Contract(sources.find(s => s.id === 'mainnet').contracts.vault, vaultAbi, signer)
const telosName = n => BigInt(Name.from(n).value.toString())
const me = await signer.getAddress()
// ETH to the WAX account alice.wam, back to me if WAX never pays
const memo = hexlify(toUtf8Bytes(`1181148696416462999:alice.wam|1:${me}`))
await vault.depositNative(telosName('hop.alcor'), me, 0, 0, memo, { value: 10n ** 16n })
// 50 USDC to the Telos account alice (approve the vault first)
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
await vault.deposit(USDC, 50_000000n, telosName('alice'), me, 0, 0, '0x')
deposit(token, amount, telosTo, refundTo, fillFee, filler, memo) for an
ERC-20, after approve(vault, amount).
depositNative(telosTo, refundTo, fillFee, filler, memo) payable, for ETH or BNB.
| Parameter | Type | Description |
|---|---|---|
| token | address | ERC-20 address from Routes (how.deposit.token) |
| amount | uint256 | Raw token units |
| telosTo | uint64 | Who receives on Telos: an account, or hop.alcor to go on to another chain |
| refundTo | address | Where the deposit goes back if it cannot be delivered. Must not be zero |
| fillFee, filler | uint64 | Reserved. Pass 0 |
| memo | bytes | Up to 256 bytes, see below |
Where it lands depends on telosTo and memo:
- A Telos account, empty memo: credited to that account on the ledger. The owner collects it with
bridge.alcor::claim(see Telos actions). - A Telos account or contract, with a memo: delivered to it as a
wrap.alcortransfer carrying that memo. This is how a deposit pays a contract. - To WAX:
telosTo = hop.alcor, memo<WAX domain>:<WAX account>|<this chain's domain>:<your address>. The first half is where it goes, the second where it comes back if WAX never pays it. The tokens arrive on WAX by themselves.
USDT on Ethereum refuses to change a non-zero allowance: set it to 0 first.
A deposit the vault refuses reverts with a custom error from the ABI:
DepositsPaused, AssetDisabled, BelowMinimum(minDeposit), CapExceeded,
AmountTooSmall, MemoTooLong, ZeroAccount, ZeroRefundAddress,
UseDepositNative (the native coin through deposit).
The Deposit event gives the key: d:<chainId>:<nonce>.
Deposit from WAX
# 100 USDC from WAX to the Telos account alice
cleos -u https://wax.greymass.com push action wrap.alcor transfer \
'["bob.wam", "bridge.alcor", "100.000000 USDC", "alice"]' -p bob.wam
# 100 USDC from WAX to an Ethereum address
cleos -u https://wax.greymass.com push action wrap.alcor transfer \
'["bob.wam", "bridge.alcor", "100.000000 USDC", "hop.alcor:1:0x285202c8db763db06ab42e0705104cb7919ef95e|1181148696416462999:bob.wam"]' -p bob.wam
// wharfkit session on WAX
await session.transact({
action: {
account: 'wrap.alcor', name: 'transfer',
authorization: [session.permissionLevel],
data: { from: session.actor, to: 'bridge.alcor', quantity: '100.000000 USDC', memo: 'alice' },
},
})
A plain transfer to bridge.alcor on WAX. The token is the route's
antelopeToken in Chains: eosio.token for WAX, wrap.alcor for
the mirrors. The memo says where it goes:
<telos account>: credited on Telos, collected withclaim.<telos account>:<note>: delivered to that account as a transfer with<note>as its memo.hop.alcor:<domain>:0x<address>|1181148696416462999:<your WAX account>: lands on Ethereum (1) or BSC (56). The last leg is a withdrawal you release yourself, see Release on Ethereum or BSC. The second half is where it comes back if that leg is never released.
The deposit's key is d:1181148696416462999:<nonce>, the nonce from the
vault's deplog action in your transaction.
Withdraw from Telos
# 50 USDT from Telos to an Ethereum address
cleos -u https://mainnet.telos.net push action wrap.alcor transfer \
'["alice", "bridge.alcor", "50.000000 USDT", "1:0x285202c8db763db06ab42e0705104cb7919ef95e"]' -p alice
# 10 WAX from Telos to the WAX account bob.wam
cleos -u https://mainnet.telos.net push action wrap.alcor transfer \
'["alice", "bridge.alcor", "10.00000000 WAX", "1181148696416462999:bob.wam"]' -p alice
A transfer of a wrap.alcor token to bridge.alcor on Telos, with the
destination in the memo: <domain>:<recipient>[:<fee>].
- Into WAX the relayer pays it out, in about two minutes. Nothing else to do.
- Into Ethereum or BSC the withdrawal waits for you to release it on that chain, see the next section.
feeis optional, a whole number of Telos units out of the amount. It goes to whoever sends the release, so a recipient with no gas on the destination chain can still be paid by someone else. Without it, the fee is zero.
The amount must not exceed withdrawableNow of the route in Chains:
each vault lets only so much out per day.
To attach data for the receiver, use wrap.alcor::withdraw(owner, quantity, domain, recipient, fee, memo)
instead of a transfer: recipient is the 20 address bytes, fee an asset in
the same token, and memo up to 1024 bytes. The memo is proved with the
amount and comes out in the Released event of AlcorWithdrawals.
The withdrawal id is in the inline bridge.alcor::wdlog action of your
transaction: wdlog(id, domain, vault, token, recipient, amount, deadline, fee, memo).
The key is w:<id>.
Release on Ethereum or BSC
const wdAbi = await (await fetch('https://api.alcor.exchange/abi/AlcorWithdrawals.json')).json()
const p = await (await fetch(`${API}/proofs/w:${id}`)).json() // 404 until the proof exists
if (p.track === 'light') {
// a root covering this burn is already trusted: ~145-180k gas
await new Contract(p.light.releaseTo, wdAbi, signer).release(p.light.release)
} else {
// the root is submitted in the same transaction: ~2.6M gas
const s = p.hard.submitRoot
await new Contract(p.hard.releaseTo, wdAbi, signer)
.submitAndRelease(s.finality, s.bits, s.signature, s.policyKeys, s.pending, p.hard.release)
}
A withdrawal to an EVM chain is paid out when someone sends its proof to
AlcorWithdrawals. Anyone may send it: every field is checked against a root
signed by Telos finalizers, and the tokens go only to the recipient the burn
named.
- Wait until
GET /v1/transfers/w:<id>showshasProof: true, about a minute after the burn. - Fetch
GET /v1/proofs/w:<id>. Its objects are in Solidity struct order and carry both contract addresses. Pass them through unchanged. - Send
releaseon thelighttrack orsubmitAndReleaseon thehardtrack. One transaction either way.
Deadline. The proof carries a deadline, about a week after the burn.
Past it release reverts with PastDeadline. Send the same body to expire (or
submitAndExpire) instead, and the tokens come back to their owner on Telos.
Daily limit. A release over the vault's remaining daily allowance reverts
with OutflowLimited and consumes nothing. The same proof works once the
allowance refills, any time before the deadline.
Fees. When the withdrawal set a fee, msg.sender receives it in the same
transaction. GET /v1/pending lists every withdrawal ready to settle right now,
with action saying release or expire.
Telos actions
# Collect a credited deposit
cleos -u https://mainnet.telos.net push action bridge.alcor claim '["alice", "USDC"]' -p alice
| Action | Who may call | What it does |
|---|---|---|
bridge.alcor::claim(owner, sym_code) |
The owner | Transfers a credited deposit to its owner. Table claims, scope = owner |
bridge.alcor::forward(domain, nonce) |
Anyone | Delivers a deposit that carried a memo. The relayer does it; call it if you do not want to wait |
bridge.alcor::bounce(domain, nonce) |
Anyone, an hour after arrival | Sends an undeliverable deposit back to its refundTo |
hop.alcor::recover(id) |
Anyone | Sends a hop that came back from its destination on to its way-back address |
<gateway>::credit |
Anyone, with a proof | Credits a deposit. The relayer does it. To do it yourself, send the actions of GET /v1/proofs/d:<domain>:<nonce> in one transaction |
Tables worth reading on bridge.alcor: routes (limits and backing per route),
claims (credited, not yet collected), wdraws (withdrawals and their status).
Contract events
| Contract | Event | Meaning |
|---|---|---|
AlcorVault |
Deposit(token, from, amount, canonical, telosTo, nonce, refundTo, fillFee, filler, memo) |
Deposit locked. Key d:<chainId>:<nonce> |
AlcorVault |
Released(id, token, to, amount, feeTo, fee) |
Withdrawal w:<id> paid out, raw units |
AlcorVault |
Voided(id) |
Withdrawal w:<id> expired, refunded on Telos |
AlcorWithdrawals |
Released(id, token, to, amount, memo) |
The same payout with the Telos memo, in Telos units |
AlcorWithdrawals |
Expired(id) |
expire accepted |
WAX bridge.alcor |
action deplog(nonce, bridge, token_contract, token_symbol, from, raw, canonical, beneficiary, memo) |
Deposit from WAX. Key d:1181148696416462999:<nonce> |
Telos bridge.alcor |
action wdlog(id, domain, vault, token, recipient, amount, deadline, fee, memo) |
Withdrawal opened. Key w:<id> |
Transfers and timing
curl https://telos.alcor.exchange/api/bridge/v1/transfers/d:56:1
curl "https://telos.alcor.exchange/api/bridge/v1/transfers?address=0x285202c8db763db06ab42e0705104cb7919ef95e&limit=20"
GET /v1/transfers/<key> is one leg; GET /v1/transfers?account=&address=&direction=&status=&limit=&before=
lists legs, newest first (before is the src_time of the last row, for
paging). Transfer status follows a whole Ethereum ↔ WAX
journey instead. A leg moves through these statuses:
deposit seen ──▶ provable ──▶ credited
└─▶ parked ──▶ forwarded
└─▶ bounced
withdrawal burned ──▶ provable ──▶ released
└─▶ expired ──▶ refunded
Typical times, measured on recent transfers. The live figures are in
Chains under timing:
| From → to | Time | Your transactions |
|---|---|---|
| Ethereum → Telos | about 17 min, Ethereum finality | Deposit |
| Ethereum → WAX | about 20 min | Deposit |
| BSC → Telos | about 30 s | Deposit |
| BSC → WAX | about 3 min | Deposit |
| WAX → Telos | about 50 s | Transfer |
| Telos → WAX | about 2 min | Transfer |
| Telos → Ethereum or BSC | about 1 min to the proof | Transfer, then release |
| WAX → Ethereum or BSC | about 2–3 min to the proof | Transfer, then release |
Poll every 10–15 seconds: a transfer changes state on the scale of minutes.
Errors
The Kittn API uses the following error codes:
| Error Code | Meaning |
|---|---|
| 400 | Bad Request -- Your request is invalid. |
| 401 | Unauthorized -- Your API key is wrong. |
| 403 | Forbidden -- The kitten requested is hidden for administrators only. |
| 404 | Not Found -- The specified kitten could not be found. |
| 405 | Method Not Allowed -- You tried to access a kitten with an invalid method. |
| 406 | Not Acceptable -- You requested a format that isn't json. |
| 410 | Gone -- The kitten requested has been removed from our servers. |
| 418 | I'm a teapot. |
| 429 | Too Many Requests -- You're requesting too many kittens! Slow down! |
| 500 | Internal Server Error -- We had a problem with our server. Try again later. |
| 503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |