# On-chain status of a transaction on Polygon (chain ID 137). Replace TX_HASH with your transaction hash.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
TX_HASH='0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322'
METHOD=GET
ENDPOINT="/transactions/$TX_HASH"
QUERY='chainId=137'
BODY='' # GET has no body: the signature covers the empty string
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')"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type OnchainStatus = {
hash: string;
chainId: number;
status: "pending" | "success" | "reverted" | "not_found";
blockNumber: number | null;
confirmations: number;
gasUsed: string | null;
};
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the transaction is mined. Returns the last status if the deadline (epoch ms) passes first.
const waitForTransaction = async (hash: string, chainId: number, deadline: number): Promise<OnchainStatus | undefined> => {
let last: OnchainStatus | undefined;
let signedAgain = false;
while (Date.now() < deadline) {
try {
last = await olympexRequest<OnchainStatus>("GET", `/transactions/${hash}?chainId=${chainId}`); // signs each attempt
if (last.status === "success" || last.status === "reverted") return last;
} 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: try again
}
await sleep(5_000);
}
return last;
};
const tx = await waitForTransaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", // your transaction
137,
Date.now() + 5 * 60_000, // pick a deadline that fits the chain
);
console.log(tx?.status ?? "unknown", tx?.blockNumber, tx?.gasUsed);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
def wait_for_transaction(tx_hash, chain_id, deadline):
"""Poll until the transaction is mined. Return the last status if the deadline (time.monotonic()) passes first."""
last = None
signed_again = False
while time.monotonic() < deadline:
try:
last = olympex_request("GET", f"/transactions/{tx_hash}?chainId={chain_id}") # signs each attempt
if last["status"] in ("success", "reverted"):
return last
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
except requests.RequestException:
pass # network error: try again
time.sleep(5)
return last
tx = wait_for_transaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", # your transaction
137,
deadline=time.monotonic() + 5 * 60, # pick a deadline that fits the chain
)
print(tx["status"] if tx else "unknown", tx and tx["blockNumber"], tx and tx["gasUsed"])
{
"success": true,
"data": {
"hash": "0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322",
"chainId": 137,
"status": "success",
"blockNumber": 89993588,
"confirmations": 5183189,
"gasUsed": "1771889"
},
"meta": {
"requestId": "E7etVi19oAMEbXQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": true,
"data": {
"hash": "0xabababababababababababababababababababababababababababababababab",
"chainId": 137,
"status": "not_found",
"blockNumber": null,
"confirmations": 0,
"gasUsed": null
},
"meta": {
"requestId": "E7ehjiyBIAMEZWw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid transaction hash",
"details": [
{
"field": "hash",
"message": "hash must be a 0x-prefixed 32-byte hex string"
}
]
},
"meta": {
"requestId": "E7ehpjcyoAMEZUw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid query parameters",
"details": [
{
"field": "chainId",
"message": "Invalid input: expected number, received NaN"
}
]
},
"meta": {
"requestId": "E7ehvhqOIAMEZUQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
Get on-chain transaction status
Check whether a transaction you broadcast is pending, succeeded, reverted or unknown, on any enabled chain.
# On-chain status of a transaction on Polygon (chain ID 137). Replace TX_HASH with your transaction hash.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
TX_HASH='0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322'
METHOD=GET
ENDPOINT="/transactions/$TX_HASH"
QUERY='chainId=137'
BODY='' # GET has no body: the signature covers the empty string
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')"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type OnchainStatus = {
hash: string;
chainId: number;
status: "pending" | "success" | "reverted" | "not_found";
blockNumber: number | null;
confirmations: number;
gasUsed: string | null;
};
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the transaction is mined. Returns the last status if the deadline (epoch ms) passes first.
const waitForTransaction = async (hash: string, chainId: number, deadline: number): Promise<OnchainStatus | undefined> => {
let last: OnchainStatus | undefined;
let signedAgain = false;
while (Date.now() < deadline) {
try {
last = await olympexRequest<OnchainStatus>("GET", `/transactions/${hash}?chainId=${chainId}`); // signs each attempt
if (last.status === "success" || last.status === "reverted") return last;
} 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: try again
}
await sleep(5_000);
}
return last;
};
const tx = await waitForTransaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", // your transaction
137,
Date.now() + 5 * 60_000, // pick a deadline that fits the chain
);
console.log(tx?.status ?? "unknown", tx?.blockNumber, tx?.gasUsed);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
def wait_for_transaction(tx_hash, chain_id, deadline):
"""Poll until the transaction is mined. Return the last status if the deadline (time.monotonic()) passes first."""
last = None
signed_again = False
while time.monotonic() < deadline:
try:
last = olympex_request("GET", f"/transactions/{tx_hash}?chainId={chain_id}") # signs each attempt
if last["status"] in ("success", "reverted"):
return last
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
except requests.RequestException:
pass # network error: try again
time.sleep(5)
return last
tx = wait_for_transaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", # your transaction
137,
deadline=time.monotonic() + 5 * 60, # pick a deadline that fits the chain
)
print(tx["status"] if tx else "unknown", tx and tx["blockNumber"], tx and tx["gasUsed"])
{
"success": true,
"data": {
"hash": "0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322",
"chainId": 137,
"status": "success",
"blockNumber": 89993588,
"confirmations": 5183189,
"gasUsed": "1771889"
},
"meta": {
"requestId": "E7etVi19oAMEbXQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": true,
"data": {
"hash": "0xabababababababababababababababababababababababababababababababab",
"chainId": 137,
"status": "not_found",
"blockNumber": null,
"confirmations": 0,
"gasUsed": null
},
"meta": {
"requestId": "E7ehjiyBIAMEZWw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid transaction hash",
"details": [
{
"field": "hash",
"message": "hash must be a 0x-prefixed 32-byte hex string"
}
]
},
"meta": {
"requestId": "E7ehpjcyoAMEZUw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid query parameters",
"details": [
{
"field": "chainId",
"message": "Invalid input: expected number, received NaN"
}
]
},
"meta": {
"requestId": "E7ehvhqOIAMEZUQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
GET /transactions/{hash} returns the on-chain status of a transaction on the chain you name in ?chainId=: pending, success, reverted or not_found, with its block number, confirmations and gas used once it’s mined. It works for any transaction on a chain from GET /chains, not only Olympex swaps: use it to confirm an approval, or a swap you sent with calldata from POST /swap. For the bridge leg of a cross-chain transfer, use POST /tx-status.
When to use it
| You want to know | Call |
|---|---|
| Whether a transaction you broadcast was mined, and whether it succeeded: a single-chain swap, an approval, or the source transaction of a cross-chain transfer. | GET /transactions/{hash}?chainId=, with the chain you sent it on. |
| Whether a cross-chain transfer arrived on the destination chain. | POST /tx-status, with the source transaction hash, the source chain ID and the dexHash from the cross-chain POST /swap. |
POST /tx-status until the transfer is final.
Read the status
status | Meaning | What to do |
|---|---|---|
pending | The transaction is in the mempool: broadcast, but not mined yet. | Keep polling. |
success | Mined, and the transaction succeeded. | Stop polling, or keep polling until confirmations reaches the number you need. For a cross-chain transfer, start polling POST /tx-status. |
reverted | Mined, but the transaction reverted: only gas was spent. | Stop polling and show the failure. For a swap, request a new quote before you try again. |
not_found | The node doesn’t know the hash on this chain: the transaction isn’t broadcast yet, was dropped, or was sent on another chain. | Right after you broadcast, keep polling. If it persists, check the hash and the chainId, and whether your wallet dropped or replaced the transaction. |
blockNumber and gasUsed are null, and confirmations is 0, until the transaction is mined. confirmations counts the block that included the transaction. gasUsed is a decimal string, and hash comes back in lowercase.
Poll after you broadcast
- Start right away. An unknown hash returns
200withnot_found, not an error, so you can poll as soon as you broadcast. - Poll at a steady pace, for example every 5 to 15 seconds depending on the chain’s block time. Results are cached for a few seconds, so polling faster returns the same answer.
- Sign every attempt again. Nonces are single-use, and the reference clients sign each call for you.
- Retry
500 TX_STATUS_ERRORwith backoff. It means the request to the chain’s RPC failed, not that the transaction failed. Stop on a400: fix the hash or thechainIdinstead of retrying. - Set a deadline that fits the chain. If the status is still
pendingornot_foundwhen it passes, check the transaction in a block explorer and in your wallet.
chainId returns not_found, exactly like a transaction that was never broadcast, so always send the chain you broadcast on. It reports only the receipt status: it doesn’t check which contract the transaction called, so success doesn’t prove that the transaction was an Olympex swap.# On-chain status of a transaction on Polygon (chain ID 137). Replace TX_HASH with your transaction hash.
# Needs openssl, curl and OLYMPEX_API_KEY_ID, OLYMPEX_SECRET_KEY, OLYMPEX_PASSPHRASE.
TX_HASH='0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322'
METHOD=GET
ENDPOINT="/transactions/$TX_HASH"
QUERY='chainId=137'
BODY='' # GET has no body: the signature covers the empty string
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')"
import { OlympexApiError, olympexRequest } from "./sign-request.ts"; // /authentication/sign-requests
type OnchainStatus = {
hash: string;
chainId: number;
status: "pending" | "success" | "reverted" | "not_found";
blockNumber: number | null;
confirmations: number;
gasUsed: string | null;
};
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// Polls until the transaction is mined. Returns the last status if the deadline (epoch ms) passes first.
const waitForTransaction = async (hash: string, chainId: number, deadline: number): Promise<OnchainStatus | undefined> => {
let last: OnchainStatus | undefined;
let signedAgain = false;
while (Date.now() < deadline) {
try {
last = await olympexRequest<OnchainStatus>("GET", `/transactions/${hash}?chainId=${chainId}`); // signs each attempt
if (last.status === "success" || last.status === "reverted") return last;
} 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: try again
}
await sleep(5_000);
}
return last;
};
const tx = await waitForTransaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", // your transaction
137,
Date.now() + 5 * 60_000, // pick a deadline that fits the chain
);
console.log(tx?.status ?? "unknown", tx?.blockNumber, tx?.gasUsed);
import time
import requests
from sign_request import OlympexApiError, olympex_request # /authentication/sign-requests
def wait_for_transaction(tx_hash, chain_id, deadline):
"""Poll until the transaction is mined. Return the last status if the deadline (time.monotonic()) passes first."""
last = None
signed_again = False
while time.monotonic() < deadline:
try:
last = olympex_request("GET", f"/transactions/{tx_hash}?chainId={chain_id}") # signs each attempt
if last["status"] in ("success", "reverted"):
return last
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
except requests.RequestException:
pass # network error: try again
time.sleep(5)
return last
tx = wait_for_transaction(
"0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322", # your transaction
137,
deadline=time.monotonic() + 5 * 60, # pick a deadline that fits the chain
)
print(tx["status"] if tx else "unknown", tx and tx["blockNumber"], tx and tx["gasUsed"])
{
"success": true,
"data": {
"hash": "0xcf2337ce50fd03cc2d8f9a312ff1409adf96a7468e7b0ecd352203088d884322",
"chainId": 137,
"status": "success",
"blockNumber": 89993588,
"confirmations": 5183189,
"gasUsed": "1771889"
},
"meta": {
"requestId": "E7etVi19oAMEbXQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": true,
"data": {
"hash": "0xabababababababababababababababababababababababababababababababab",
"chainId": 137,
"status": "not_found",
"blockNumber": null,
"confirmations": 0,
"gasUsed": null
},
"meta": {
"requestId": "E7ehjiyBIAMEZWw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid transaction hash",
"details": [
{
"field": "hash",
"message": "hash must be a 0x-prefixed 32-byte hex string"
}
]
},
"meta": {
"requestId": "E7ehpjcyoAMEZUw=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid query parameters",
"details": [
{
"field": "chainId",
"message": "Invalid input: expected number, received NaN"
}
]
},
"meta": {
"requestId": "E7ehvhqOIAMEZUQ=",
"version": "v1",
"accountType": "integrator",
"apiKeyId": "00000000-0000-4000-8000-000000000000"
}
}
{
"message": "Forbidden"
}
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.
Path Parameters
Transaction hash: 0x and 64 hexadecimal characters.
^0x[0-9a-fA-F]{64}$Query Parameters
Chain ID, for example 137. It must be one of the chains from GET /chains.
x >= 1Was this page helpful?
