Cancel a pending withdrawal order to recover locked vault shares. Only the original order owner (verified on-chain via msg.sender) can cancel.
Only orders with status PENDING can be cancelled. Orders that have already
been processed (COMPLETE) or are mid-refund (PENDING_REFUND, REFUNDED)
cannot be cancelled.
Prerequisites
- A Paxos Labs API key
- An EVM-compatible signer (the same address that submitted the original withdrawal order)
- The
orderIndex of the pending withdrawal order
Step 1: Find the Order Index
Query GET /v2/amplify/withdrawalRequests to find the orderIndex of the order you want to cancel:
curl "https://api.paxoslabs.com/v2/amplify/withdrawalRequests?\
filter=userAddress%3D0x1234567890abcdef1234567890abcdef12345678%20AND%20\
vaultAddress%3D0xbbbb000000000000000000000000000000000001%20AND%20\
status%3DPENDING" \
-H "x-api-key: pxl_your_key"
Each item in the response includes an orderIndex field:
{
"withdrawalRequests": [
{
"id": "wr_abc123",
"orderIndex": "42",
"status": "PENDING",
"vaultAddress": "0xbbbb...",
"chainId": 1,
"userAddress": "0x1234...",
"orderAmount": "1000000000000000000",
"wantAssetAddress": "0xA0b8..."
}
],
"nextPageToken": null
}
Step 2: Prepare Cancel Calldata
GET /v2/amplify/withdraw/cancel returns the transaction to cancel a pending withdrawal order.
Parameters
| Parameter | Type | Required | Description |
|---|
vaultAddress | string | Yes | BoringVault contract address (0x + 40 hex chars) |
orderIndex | string | Yes | Index of the withdrawal order to cancel (decimal string from withdrawalRequests response) |
chainId | number | Yes | EVM chain ID |
responseFormat | string | No | encoded (default), full, or structured |
Response
{
"transaction": {
"to": "0xdddd000000000000000000000000000000000001",
"data": "0x5c975abb...",
"value": "0",
"abi": [{"type": "function", "name": "cancelOrder", "inputs": [...]}],
"functionName": "cancelOrder",
"args": ["42"]
}
}
Step 3: Sign and Submit
Broadcast the cancellation transaction. The transaction must be sent from the same address that originally submitted the withdrawal order — ownership is enforced on-chain via msg.sender.
After confirmation, the locked vault shares are returned to the user’s wallet.
Complete Examples
import { createWalletClient, createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const API_KEY = process.env.AMPLIFY_API_KEY!;
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const BASE = "https://api.paxoslabs.com";
const HEADERS = { "x-api-key": API_KEY };
const VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
const CHAIN_ID = 1;
async function main() {
const account = privateKeyToAccount(PRIVATE_KEY);
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http(),
});
const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});
// Step 1: Find pending withdrawal orders
console.log("Fetching pending withdrawal orders...");
const statusUrl = new URL(`${BASE}/v2/amplify/withdrawalRequests`);
statusUrl.searchParams.set(
"filter",
`userAddress=${account.address} AND vaultAddress=${VAULT_ADDRESS} AND status=PENDING`
);
const statusResp = await fetch(statusUrl, { headers: HEADERS }).then(
(r) => r.json()
);
const orders = statusResp.withdrawalRequests;
if (orders.length === 0) {
console.log("No pending orders to cancel.");
return;
}
const orderIndex = orders[0].orderIndex;
console.log(`Cancelling order index: ${orderIndex}`);
// Step 2: Get cancel calldata
const cancelUrl = new URL(`${BASE}/v2/amplify/withdraw/cancel`);
cancelUrl.searchParams.set("vaultAddress", VAULT_ADDRESS);
cancelUrl.searchParams.set("orderIndex", orderIndex);
cancelUrl.searchParams.set("chainId", String(CHAIN_ID));
const cancelResp = await fetch(cancelUrl, { headers: HEADERS }).then(
(r) => r.json()
);
const tx = cancelResp.transaction;
// Step 3: Sign and submit
console.log("Submitting cancellation...");
const hash = await walletClient.sendTransaction({
to: tx.to as `0x${string}`,
data: tx.data as `0x${string}`,
value: BigInt(tx.value),
chain: mainnet,
account,
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(
`Cancellation confirmed in block ${receipt.blockNumber}: ${hash}`
);
}
main().catch(console.error);
import os
import requests
from web3 import Web3
API_KEY = os.environ["AMPLIFY_API_KEY"]
PRIVATE_KEY = os.environ["PRIVATE_KEY"]
BASE = "https://api.paxoslabs.com"
HEADERS = {"x-api-key": API_KEY}
VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001"
CHAIN_ID = 1
w3 = Web3(Web3.HTTPProvider("https://eth.llamarpc.com"))
account = w3.eth.account.from_key(PRIVATE_KEY)
# Step 1: Find pending withdrawal orders
print("Fetching pending withdrawal orders...")
status_resp = requests.get(f"{BASE}/v2/amplify/withdrawalRequests", headers=HEADERS, params={
"filter": f"userAddress={account.address} AND vaultAddress={VAULT_ADDRESS} AND status=PENDING",
}).json()
orders = status_resp["withdrawalRequests"]
if not orders:
print("No pending orders to cancel.")
exit()
order_index = orders[0]["orderIndex"]
print(f"Cancelling order index: {order_index}")
# Step 2: Get cancel calldata
cancel_resp = requests.get(f"{BASE}/v2/amplify/withdraw/cancel", headers=HEADERS, params={
"vaultAddress": VAULT_ADDRESS,
"orderIndex": order_index,
"chainId": CHAIN_ID,
}).json()
tx = cancel_resp["transaction"]
# Step 3: Sign and submit
print("Submitting cancellation...")
cancel_tx = {
"to": Web3.to_checksum_address(tx["to"]),
"data": tx["data"],
"value": int(tx["value"]),
"gas": 200_000,
"nonce": w3.eth.get_transaction_count(account.address),
"chainId": CHAIN_ID,
}
signed = account.sign_transaction(cancel_tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Cancellation confirmed in block {receipt['blockNumber']}: {tx_hash.hex()}")
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"net/url"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
const (
baseURL = "https://api.paxoslabs.com"
vaultAddress = "0xbbbb000000000000000000000000000000000001"
chainID = 1
)
func apiGet(path string, params url.Values) (map[string]interface{}, error) {
u := fmt.Sprintf("%s%s?%s", baseURL, path, params.Encode())
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("x-api-key", os.Getenv("AMPLIFY_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
func main() {
pk, _ := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
fromAddr := crypto.PubkeyToAddress(pk.PublicKey)
client, _ := ethclient.Dial("https://eth.llamarpc.com")
ctx := context.Background()
signer := types.NewEIP155Signer(big.NewInt(chainID))
// Step 1: Find pending withdrawal orders
fmt.Println("Fetching pending withdrawal orders...")
statusResp, _ := apiGet("/v2/amplify/withdrawalRequests", url.Values{
"filter": {fmt.Sprintf("userAddress=%s AND vaultAddress=%s AND status=PENDING",
fromAddr.Hex(), vaultAddress)},
})
orders := statusResp["withdrawalRequests"].([]interface{})
if len(orders) == 0 {
fmt.Println("No pending orders to cancel.")
return
}
order := orders[0].(map[string]interface{})
orderIndex := order["orderIndex"].(string)
fmt.Printf("Cancelling order index: %s\n", orderIndex)
// Step 2: Get cancel calldata
fmt.Println("Fetching cancel calldata...")
cancelResp, _ := apiGet("/v2/amplify/withdraw/cancel", url.Values{
"vaultAddress": {vaultAddress},
"orderIndex": {orderIndex},
"chainId": {fmt.Sprint(chainID)},
})
txData := cancelResp["transaction"].(map[string]interface{})
// Step 3: Sign and submit
fmt.Println("Submitting cancellation...")
nonce, _ := client.PendingNonceAt(ctx, fromAddr)
gasPrice, _ := client.SuggestGasPrice(ctx)
cancelTx := types.NewTransaction(nonce,
common.HexToAddress(txData["to"].(string)),
big.NewInt(0), 200000, gasPrice,
common.FromHex(txData["data"].(string)))
signedCancel, _ := types.SignTx(cancelTx, signer, pk)
client.SendTransaction(ctx, signedCancel)
fmt.Printf("Cancellation submitted: %s\n", signedCancel.Hash().Hex())
}
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class AmplifyCancelWithdraw {
static final String BASE = "https://api.paxoslabs.com";
static final String API_KEY = System.getenv("AMPLIFY_API_KEY");
static final String VAULT_ADDRESS = "0xbbbb000000000000000000000000000000000001";
static final int CHAIN_ID = 1;
static HttpClient httpClient = HttpClient.newHttpClient();
static JsonObject apiGet(String path) throws Exception {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(BASE + path))
.header("x-api-key", API_KEY)
.GET().build();
HttpResponse<String> resp = httpClient.send(req,
HttpResponse.BodyHandlers.ofString());
return JsonParser.parseString(resp.body()).getAsJsonObject();
}
public static void main(String[] args) throws Exception {
Credentials credentials = Credentials.create(System.getenv("PRIVATE_KEY"));
String userAddress = credentials.getAddress();
Web3j web3 = Web3j.build(new HttpService("https://eth.llamarpc.com"));
// Step 1: Find pending withdrawal orders
System.out.println("Fetching pending withdrawal orders...");
String filter = String.format(
"userAddress=%s AND vaultAddress=%s AND status=PENDING",
userAddress, VAULT_ADDRESS);
String statusPath = "/v2/amplify/withdrawalRequests?filter=" +
java.net.URLEncoder.encode(filter, "UTF-8");
JsonObject statusResp = apiGet(statusPath);
JsonArray orders = statusResp.getAsJsonArray("withdrawalRequests");
if (orders.size() == 0) {
System.out.println("No pending orders to cancel.");
return;
}
String orderIndex = orders.get(0).getAsJsonObject()
.get("orderIndex").getAsString();
System.out.println("Cancelling order index: " + orderIndex);
// Step 2: Get cancel calldata
System.out.println("Fetching cancel calldata...");
String cancelPath = String.format(
"/v2/amplify/withdraw/cancel?vaultAddress=%s&orderIndex=%s&chainId=%d",
VAULT_ADDRESS, orderIndex, CHAIN_ID);
JsonObject cancelResp = apiGet(cancelPath);
JsonObject tx = cancelResp.getAsJsonObject("transaction");
// Step 3: Sign and submit
System.out.println("Submitting cancellation...");
BigInteger nonce = web3.ethGetTransactionCount(userAddress,
org.web3j.protocol.core.DefaultBlockParameterName.PENDING)
.send().getTransactionCount();
BigInteger gasPrice = web3.ethGasPrice().send().getGasPrice();
RawTransaction cancelTx = RawTransaction.createTransaction(
nonce, gasPrice, BigInteger.valueOf(200000),
tx.get("to").getAsString(), BigInteger.ZERO,
tx.get("data").getAsString());
byte[] signedCancel = TransactionEncoder.signMessage(
cancelTx, CHAIN_ID, credentials);
String txHash = web3.ethSendRawTransaction(
Numeric.toHexString(signedCancel)).send().getTransactionHash();
System.out.println("Cancellation submitted: " + txHash);
}
}
Error Responses
| Status | Meaning |
|---|
| 400 | Invalid parameters (bad address, missing orderIndex, etc.) |
| 404 | No account found for the given vaultAddress + chainId |