# Status of a cross-chain transfer. Replace hash with your source-chain transaction hash
# and dexHash with the value from the cross-chain POST /swap response.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
METHOD=POST
ENDPOINT=/tx-status
QUERY=''
BODY='{"chainId":137,"dexHash":"0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c","hash":"0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b"}'
TS="${OLYMPEX_TIMESTAMP:-$(date +%s)}"
NONCE="${OLYMPEX_NONCE:-$(openssl rand -hex 12)}"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
VALUE_INFO="$(printf '%s\n%s\n%s' "$TS" "$NONCE" "$BODY_HASH")"
MESSAGE="$(printf 'OLPX-HMAC-SHA256-V2\n%s\n%s\n%s\n%s\n%s\n%s' "$TS" "$NONCE" "$METHOD" "/api/v1$ENDPOINT" "$QUERY" "$BODY_HASH")"
curl -sS -X "$METHOD" "https://api-rest.olympex.io/api/v1$ENDPOINT${QUERY:+?$QUERY}" \
-H "content-type: application/json" \
-H "x-api-key-id: $OLYMPEX_API_KEY_ID" \
-H "x-value-info: $(printf '%s' "$VALUE_INFO" | openssl base64 -A)" \
-H "x-passphrase: $OLYMPEX_PASSPHRASE" \
-H "x-signature: $(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$OLYMPEX_SECRET_KEY" -binary | od -An -v -tx1 | tr -d ' \n')" \
--data-raw "$BODY"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type TxStatus = { status: string; detailStatus: string; toTxHash?: string | null; toAmount?: string };
type TxStatusBody = { hash: string; chainId: number; dexHash: string };
const SUCCESS = new Set(["SUCCESS", "DONE", "success", "Success"]);
const FAILURE = new Set(["FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"]);
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the provider reports a final status or the deadline (epoch ms) passes.
const pollTransferStatus = async (body: TxStatusBody, deadline: number): Promise<TxStatus> => {
let signedAgain = false;
for (let delay = 15_000; Date.now() + delay < deadline; delay = Math.min(delay * 2, 30_000)) {
await sleep(delay);
try {
const transfer = await olympexRequest<TxStatus>("POST", "/tx-status", body); // signs each attempt with a new nonce
if (SUCCESS.has(transfer.status) || FAILURE.has(transfer.status)) return transfer;
} catch (error) {
if (error instanceof OlympexApiError && error.status < 500) {
// A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
const gatewayAuth = error.code === "HTTP_401" || error.code === "HTTP_403";
if (!gatewayAuth || signedAgain) throw error;
signedAgain = true;
}
// TX_STATUS_ERROR, other 5xx and network errors: the status is unknown yet, so keep polling.
}
}
throw new Error("No final status before the deadline. Check a block explorer, then contact support.");
};
const transfer = await pollTransferStatus(
{
hash: "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", // your source-chain transaction
chainId: 137, // source chain ID
dexHash: "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", // from POST /swap
},
Date.now() + 30 * 60_000, // pick a deadline that fits your routes
);
console.log(SUCCESS.has(transfer.status) ? "delivered" : "failed", transfer.status, transfer.toTxHash ?? transfer.detailStatus);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
SUCCESS = {"SUCCESS", "DONE", "success", "Success"}
FAILURE = {"FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"}
def poll_transfer_status(body, deadline):
"""Poll until the provider reports a final status or the deadline (time.monotonic()) passes."""
delay = 15
signed_again = False
while time.monotonic() + delay < deadline:
time.sleep(delay)
delay = min(delay * 2, 30)
try:
transfer = olympex_request("POST", "/tx-status", body) # signs each attempt with a new nonce
except OlympexApiError as error:
if error.status < 500:
# A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
if error.code not in ("HTTP_401", "HTTP_403") or signed_again:
raise
signed_again = True
continue # TX_STATUS_ERROR or another 5xx: the status is unknown yet
except requests.RequestException:
continue # network error: try again
if transfer["status"] in SUCCESS or transfer["status"] in FAILURE:
return transfer
raise TimeoutError("No final status before the deadline. Check a block explorer, then contact support.")
transfer = poll_transfer_status(
{
"hash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", # your source-chain transaction
"chainId": 137, # source chain ID
"dexHash": "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", # from POST /swap
},
deadline=time.monotonic() + 30 * 60, # pick a deadline that fits your routes
)
print("delivered" if transfer["status"] in SUCCESS else "failed", transfer["status"], transfer.get("toTxHash") or transfer["detailStatus"])
{
"success": true,
"data": {
"fromChainId": 137,
"toChainId": 1,
"fromTxHash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b",
"toTxHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"fromAmount": "10000000",
"toAmount": "9985000",
"fromTokenAddress": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
"toTokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"bridgeHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"errorMsg": null,
"status": "SUCCESS",
"detailStatus": "SUCCESS"
},
"meta": {
"requestId": "ENWRgDn5IAMEMEw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{
"field": "hash",
"message": "Invalid input: expected string, received undefined"
},
{
"field": "chainId",
"message": "Invalid input: expected number, received undefined"
},
{
"field": "dexHash",
"message": "Invalid input: expected string, received undefined"
}
]
},
"meta": {
"requestId": "E7sKGjSOIAMEVMg=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
{
"success": false,
"error": {
"code": "TX_STATUS_ERROR",
"message": "Unexpected transaction status handler error",
"details": [
{
"message": "Could not retrieve transaction status"
}
]
},
"meta": {
"requestId": "ENUmOj8JoAMEMsw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
Get cross-chain transfer status
Track a cross-chain transfer from the source transaction to delivery on the destination chain.
# Status of a cross-chain transfer. Replace hash with your source-chain transaction hash
# and dexHash with the value from the cross-chain POST /swap response.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
METHOD=POST
ENDPOINT=/tx-status
QUERY=''
BODY='{"chainId":137,"dexHash":"0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c","hash":"0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b"}'
TS="${OLYMPEX_TIMESTAMP:-$(date +%s)}"
NONCE="${OLYMPEX_NONCE:-$(openssl rand -hex 12)}"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
VALUE_INFO="$(printf '%s\n%s\n%s' "$TS" "$NONCE" "$BODY_HASH")"
MESSAGE="$(printf 'OLPX-HMAC-SHA256-V2\n%s\n%s\n%s\n%s\n%s\n%s' "$TS" "$NONCE" "$METHOD" "/api/v1$ENDPOINT" "$QUERY" "$BODY_HASH")"
curl -sS -X "$METHOD" "https://api-rest.olympex.io/api/v1$ENDPOINT${QUERY:+?$QUERY}" \
-H "content-type: application/json" \
-H "x-api-key-id: $OLYMPEX_API_KEY_ID" \
-H "x-value-info: $(printf '%s' "$VALUE_INFO" | openssl base64 -A)" \
-H "x-passphrase: $OLYMPEX_PASSPHRASE" \
-H "x-signature: $(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$OLYMPEX_SECRET_KEY" -binary | od -An -v -tx1 | tr -d ' \n')" \
--data-raw "$BODY"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type TxStatus = { status: string; detailStatus: string; toTxHash?: string | null; toAmount?: string };
type TxStatusBody = { hash: string; chainId: number; dexHash: string };
const SUCCESS = new Set(["SUCCESS", "DONE", "success", "Success"]);
const FAILURE = new Set(["FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"]);
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the provider reports a final status or the deadline (epoch ms) passes.
const pollTransferStatus = async (body: TxStatusBody, deadline: number): Promise<TxStatus> => {
let signedAgain = false;
for (let delay = 15_000; Date.now() + delay < deadline; delay = Math.min(delay * 2, 30_000)) {
await sleep(delay);
try {
const transfer = await olympexRequest<TxStatus>("POST", "/tx-status", body); // signs each attempt with a new nonce
if (SUCCESS.has(transfer.status) || FAILURE.has(transfer.status)) return transfer;
} catch (error) {
if (error instanceof OlympexApiError && error.status < 500) {
// A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
const gatewayAuth = error.code === "HTTP_401" || error.code === "HTTP_403";
if (!gatewayAuth || signedAgain) throw error;
signedAgain = true;
}
// TX_STATUS_ERROR, other 5xx and network errors: the status is unknown yet, so keep polling.
}
}
throw new Error("No final status before the deadline. Check a block explorer, then contact support.");
};
const transfer = await pollTransferStatus(
{
hash: "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", // your source-chain transaction
chainId: 137, // source chain ID
dexHash: "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", // from POST /swap
},
Date.now() + 30 * 60_000, // pick a deadline that fits your routes
);
console.log(SUCCESS.has(transfer.status) ? "delivered" : "failed", transfer.status, transfer.toTxHash ?? transfer.detailStatus);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
SUCCESS = {"SUCCESS", "DONE", "success", "Success"}
FAILURE = {"FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"}
def poll_transfer_status(body, deadline):
"""Poll until the provider reports a final status or the deadline (time.monotonic()) passes."""
delay = 15
signed_again = False
while time.monotonic() + delay < deadline:
time.sleep(delay)
delay = min(delay * 2, 30)
try:
transfer = olympex_request("POST", "/tx-status", body) # signs each attempt with a new nonce
except OlympexApiError as error:
if error.status < 500:
# A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
if error.code not in ("HTTP_401", "HTTP_403") or signed_again:
raise
signed_again = True
continue # TX_STATUS_ERROR or another 5xx: the status is unknown yet
except requests.RequestException:
continue # network error: try again
if transfer["status"] in SUCCESS or transfer["status"] in FAILURE:
return transfer
raise TimeoutError("No final status before the deadline. Check a block explorer, then contact support.")
transfer = poll_transfer_status(
{
"hash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", # your source-chain transaction
"chainId": 137, # source chain ID
"dexHash": "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", # from POST /swap
},
deadline=time.monotonic() + 30 * 60, # pick a deadline that fits your routes
)
print("delivered" if transfer["status"] in SUCCESS else "failed", transfer["status"], transfer.get("toTxHash") or transfer["detailStatus"])
{
"success": true,
"data": {
"fromChainId": 137,
"toChainId": 1,
"fromTxHash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b",
"toTxHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"fromAmount": "10000000",
"toAmount": "9985000",
"fromTokenAddress": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
"toTokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"bridgeHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"errorMsg": null,
"status": "SUCCESS",
"detailStatus": "SUCCESS"
},
"meta": {
"requestId": "ENWRgDn5IAMEMEw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{
"field": "hash",
"message": "Invalid input: expected string, received undefined"
},
{
"field": "chainId",
"message": "Invalid input: expected number, received undefined"
},
{
"field": "dexHash",
"message": "Invalid input: expected string, received undefined"
}
]
},
"meta": {
"requestId": "E7sKGjSOIAMEVMg=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
{
"success": false,
"error": {
"code": "TX_STATUS_ERROR",
"message": "Unexpected transaction status handler error",
"details": [
{
"message": "Could not retrieve transaction status"
}
]
},
"meta": {
"requestId": "ENUmOj8JoAMEMsw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
POST /tx-status returns the status of a cross-chain transfer, as reported by the provider that executes it. Call it after you broadcast the transaction from a cross-chain POST /swap, and poll until the status is final. For the on-chain result of a transaction itself, including a single-chain swap or the source transaction of a transfer, use GET /transactions/{hash}.
Request body
| Field | Value |
|---|---|
hash | Hash of the transaction you broadcast on the source chain. |
chainId | Source chain ID as an integer, for example 137. A string such as "137" returns 400 VALIDATION_ERROR. |
dexHash | The dexHash from the cross-chain POST /swap response, in lowercase. It identifies the provider. |
dexHash with the transaction hash as soon as you broadcast. POST /swap is the only response that carries it, and every status call needs both.Read the status
status and detailStatus are the provider’s own values, and different providers spell the same outcome differently. Compare status against these exact values:
| Outcome | status values | What to do |
|---|---|---|
| Success | SUCCESS, DONE, success, Success | Stop polling. The transfer completed. toTxHash and toAmount appear when the provider reports them. |
| Failure | FAILURE, FAILED, failed, REFUND, Reverted, FROM_FAILURE, INVALID | Stop polling and show detailStatus. Check the source transaction in a block explorer, and contact partners@olympex.io with meta.requestId if you need help. |
| In progress | Any other value | Keep polling. |
detailStatus is the provider’s sub-status or message. Display it, but don’t branch on it.
| Response field | Meaning |
|---|---|
fromChainId, toChainId | Source and destination chain IDs, as integers. |
fromTxHash, toTxHash | Source and destination transaction hashes. toTxHash appears once the provider knows it. |
fromAmount, toAmount | Amounts sent and received, passed through from the provider without conversion. Check the unit before you do arithmetic with them. |
fromTokenAddress, toTokenAddress | Source and destination token addresses. |
bridgeHash | A transfer identifier from the provider. Depending on the provider it can equal the source or the destination transaction hash. |
errorMsg | Always null in a 200 response. When the provider reports an error message, the endpoint returns 500 TX_STATUS_ERROR instead (see the Note below). Display detailStatus, not errorMsg. |
status, detailStatus, fromChainId and toChainId are always present. The other fields appear when the provider reports them.
Poll until the status is final
- Confirm the source transaction first. Read its receipt from your RPC, or poll
GET /transactions/{hash}. If it reverted, nothing left the source chain, so there is nothing to poll. - Poll with backoff, for example every 15 to 30 seconds. Sign every attempt again: nonces are single-use, and the reference clients sign each call for you.
- Keep polling through
500 TX_STATUS_ERROR, other5xxresponses and network errors. Stop on a4xx: fix the request instead of retrying it unchanged. The exception is a gateway401or403: sign again once with a new nonce, and if that fails too, stop and check your credentials and server clock. - Set a deadline that fits your product. When it passes without a final status, check the source transaction in a block explorer and contact partners@olympex.io with
meta.requestIdand the transaction hash.
500 TX_STATUS_ERROR doesn’t mean the transfer failed. It means the status is unknown: it’s expected right after broadcast, and it’s also returned for some transfers the provider marks failed. Treat it as “not yet known” until your deadline. The same error comes back, and never clears, when hash, chainId or dexHash doesn’t identify a transfer: for example the destination chain ID sent instead of the source chain ID, or a dexHash that isn’t lowercase. If it persists, check all three values against your broadcast transaction and the POST /swap response.unknown at your cap instead of throwing, use waitForTransfer from Track a swap to finality. The 200 response example is illustrative.
# Status of a cross-chain transfer. Replace hash with your source-chain transaction hash
# and dexHash with the value from the cross-chain POST /swap response.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
METHOD=POST
ENDPOINT=/tx-status
QUERY=''
BODY='{"chainId":137,"dexHash":"0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c","hash":"0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b"}'
TS="${OLYMPEX_TIMESTAMP:-$(date +%s)}"
NONCE="${OLYMPEX_NONCE:-$(openssl rand -hex 12)}"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
VALUE_INFO="$(printf '%s\n%s\n%s' "$TS" "$NONCE" "$BODY_HASH")"
MESSAGE="$(printf 'OLPX-HMAC-SHA256-V2\n%s\n%s\n%s\n%s\n%s\n%s' "$TS" "$NONCE" "$METHOD" "/api/v1$ENDPOINT" "$QUERY" "$BODY_HASH")"
curl -sS -X "$METHOD" "https://api-rest.olympex.io/api/v1$ENDPOINT${QUERY:+?$QUERY}" \
-H "content-type: application/json" \
-H "x-api-key-id: $OLYMPEX_API_KEY_ID" \
-H "x-value-info: $(printf '%s' "$VALUE_INFO" | openssl base64 -A)" \
-H "x-passphrase: $OLYMPEX_PASSPHRASE" \
-H "x-signature: $(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$OLYMPEX_SECRET_KEY" -binary | od -An -v -tx1 | tr -d ' \n')" \
--data-raw "$BODY"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type TxStatus = { status: string; detailStatus: string; toTxHash?: string | null; toAmount?: string };
type TxStatusBody = { hash: string; chainId: number; dexHash: string };
const SUCCESS = new Set(["SUCCESS", "DONE", "success", "Success"]);
const FAILURE = new Set(["FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"]);
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the provider reports a final status or the deadline (epoch ms) passes.
const pollTransferStatus = async (body: TxStatusBody, deadline: number): Promise<TxStatus> => {
let signedAgain = false;
for (let delay = 15_000; Date.now() + delay < deadline; delay = Math.min(delay * 2, 30_000)) {
await sleep(delay);
try {
const transfer = await olympexRequest<TxStatus>("POST", "/tx-status", body); // signs each attempt with a new nonce
if (SUCCESS.has(transfer.status) || FAILURE.has(transfer.status)) return transfer;
} catch (error) {
if (error instanceof OlympexApiError && error.status < 500) {
// A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
const gatewayAuth = error.code === "HTTP_401" || error.code === "HTTP_403";
if (!gatewayAuth || signedAgain) throw error;
signedAgain = true;
}
// TX_STATUS_ERROR, other 5xx and network errors: the status is unknown yet, so keep polling.
}
}
throw new Error("No final status before the deadline. Check a block explorer, then contact support.");
};
const transfer = await pollTransferStatus(
{
hash: "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", // your source-chain transaction
chainId: 137, // source chain ID
dexHash: "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", // from POST /swap
},
Date.now() + 30 * 60_000, // pick a deadline that fits your routes
);
console.log(SUCCESS.has(transfer.status) ? "delivered" : "failed", transfer.status, transfer.toTxHash ?? transfer.detailStatus);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
SUCCESS = {"SUCCESS", "DONE", "success", "Success"}
FAILURE = {"FAILURE", "FAILED", "failed", "REFUND", "Reverted", "FROM_FAILURE", "INVALID"}
def poll_transfer_status(body, deadline):
"""Poll until the provider reports a final status or the deadline (time.monotonic()) passes."""
delay = 15
signed_again = False
while time.monotonic() + delay < deadline:
time.sleep(delay)
delay = min(delay * 2, 30)
try:
transfer = olympex_request("POST", "/tx-status", body) # signs each attempt with a new nonce
except OlympexApiError as error:
if error.status < 500:
# A gateway 401 or 403 gets one new signature. Any other 4xx won't fix itself.
if error.code not in ("HTTP_401", "HTTP_403") or signed_again:
raise
signed_again = True
continue # TX_STATUS_ERROR or another 5xx: the status is unknown yet
except requests.RequestException:
continue # network error: try again
if transfer["status"] in SUCCESS or transfer["status"] in FAILURE:
return transfer
raise TimeoutError("No final status before the deadline. Check a block explorer, then contact support.")
transfer = poll_transfer_status(
{
"hash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b", # your source-chain transaction
"chainId": 137, # source chain ID
"dexHash": "0x6c2142a4113d1a2ef94717109dd5ac71f69439465f95a170fb9cf0f56d76ad7c", # from POST /swap
},
deadline=time.monotonic() + 30 * 60, # pick a deadline that fits your routes
)
print("delivered" if transfer["status"] in SUCCESS else "failed", transfer["status"], transfer.get("toTxHash") or transfer["detailStatus"])
{
"success": true,
"data": {
"fromChainId": 137,
"toChainId": 1,
"fromTxHash": "0x5f2b0a3c3e8f9d1b7a6c4e2d0f8b6a4c2e0d8f6b4a2c0e8d6f4b2a0c8e6d4f2b",
"toTxHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"fromAmount": "10000000",
"toAmount": "9985000",
"fromTokenAddress": "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
"toTokenAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"bridgeHash": "0x8e1d4c7b2a9f6e3d0c5b8a7f4e1d2c9b6a3f0e7d4c1b8a5f2e9d6c3b0a7f4e1d",
"errorMsg": null,
"status": "SUCCESS",
"detailStatus": "SUCCESS"
},
"meta": {
"requestId": "ENWRgDn5IAMEMEw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": [
{
"field": "hash",
"message": "Invalid input: expected string, received undefined"
},
{
"field": "chainId",
"message": "Invalid input: expected number, received undefined"
},
{
"field": "dexHash",
"message": "Invalid input: expected string, received undefined"
}
]
},
"meta": {
"requestId": "E7sKGjSOIAMEVMg=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
{
"success": false,
"error": {
"code": "TX_STATUS_ERROR",
"message": "Unexpected transaction status handler error",
"details": [
{
"message": "Could not retrieve transaction status"
}
]
},
"meta": {
"requestId": "ENUmOj8JoAMEMsw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
Authorizations
Your API key ID (UUID). See Sign requests.
Base64 of timestamp + "\n" + nonce + "\n" + bodyHash, where timestamp is Unix seconds, nonce is 24 new hexadecimal characters and bodyHash is the unpadded base64url SHA-256 of the canonical body.
Your account passphrase. Treat it like the secret key.
Lowercase hex HMAC-SHA256 (signature v2), keyed with your secret key string (UTF-8, not decoded), of seven lines joined with \n, with no trailing newline: OLPX-HMAC-SHA256-V2, timestamp, nonce, the uppercase method, path, canonicalQuery and bodyHash. path is the request path including /api/v1, without the query string, for example /api/v1/limit-order/<id>. canonicalQuery is the query parameters decoded (+ is a space), sorted by key and then by value, re-encoded per RFC 3986 and joined with &, or the empty string when there is no query. Headers signed for one request are rejected on any other. See Sign requests.
Body
Hash of the transaction you broadcast on the source chain.
1Source chain ID as an integer, for example 137.
0 < x <= 9007199254740991The dexHash returned by the cross-chain POST /swap, in lowercase hex. It identifies the provider.
1Was this page helpful?
