curl --request POST \
--url https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge \
--header 'Content-Type: application/json' \
--data '
{
"wallet_pubkey": "string"
}
'import requests
url = "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge"
payload = { "wallet_pubkey": "string" }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({wallet_pubkey: 'string'})
};
fetch('https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wallet_pubkey' => 'string'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge"
payload := strings.NewReader("{\n \"wallet_pubkey\": \"string\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge")
.header("Content-Type", "application/json")
.body("{\n \"wallet_pubkey\": \"string\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"wallet_pubkey\": \"string\"\n}"
response = http.request(request)
puts response.read_body{
"expires_at": "<string>",
"nonce_id": "<string>",
"unsigned_transaction": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Create wallet transaction challenge
Handles POST /v1/auth/wallet/transaction-challenge via post.v1.auth.wallet.transaction_challenge.
curl --request POST \
--url https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge \
--header 'Content-Type: application/json' \
--data '
{
"wallet_pubkey": "string"
}
'import requests
url = "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge"
payload = { "wallet_pubkey": "string" }
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({wallet_pubkey: 'string'})
};
fetch('https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'wallet_pubkey' => 'string'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge"
payload := strings.NewReader("{\n \"wallet_pubkey\": \"string\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge")
.header("Content-Type", "application/json")
.body("{\n \"wallet_pubkey\": \"string\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://perp-api.phoenix.trade/v1/auth/wallet/transaction-challenge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"wallet_pubkey\": \"string\"\n}"
response = http.request(request)
puts response.read_body{
"expires_at": "<string>",
"nonce_id": "<string>",
"unsigned_transaction": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}Body
JSON request payload for post.v1.auth.wallet.transaction_challenge.
Solana wallet pubkey the caller intends to authenticate as. The challenge transaction is built with this pubkey as the fee payer / sole signer.
Response
Challenge issued
RFC3339 timestamp at which this challenge expires.
Opaque server-issued nonce identifier. Echo on the subsequent login call.
Base64-encoded unsigned Solana legacy transaction (bincode wire format) containing a single Memo program instruction that encodes the login challenge. Wallets are allowed to include additional ComputeBudget or Lighthouse instructions before signing (some inject these automatically, in arbitrary positions), but the Memo instruction, the recent blockhash, and the wallet's required-signer slot must be preserved. The transaction uses a deterministic, non-recent blockhash and is never submitted on-chain.