Closing a Quote (Instant Close)
Instant Close Flow
Instant Close Script Example
require("dotenv").config();
const { ethers } = require("ethers");
const axios = require("axios");
// --------------------------------------------------------------------
// SIWE / Login Configuration
// --------------------------------------------------------------------
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const activeAccount = process.env.activeAccount; // Your sub-account address
const wallet = new ethers.Wallet(PRIVATE_KEY);
const SOLVER_BASE_URL = "https://www.perps-streaming.com/v1/42161a/0x141269E29a770644C34e05B127AB621511f20109";
const DOMAIN = "localhost";
const ORIGIN = "http://localhost:3000";
const CHAIN_ID = 42161;
const LOGIN_URI = `${SOLVER_BASE_URL}/login`;
const ISSUED_AT = new Date().toISOString();
// Set expiration to 24 hours from now for login
const EXPIRATION_DATE = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
// --------------------------------------------------------------------
// Helper Functions
// --------------------------------------------------------------------
// SIWE message builder (EIP-4361 format)
function buildSiweMessage({ domain, address, statement, uri, version, chainId, nonce, issuedAt, expirationTime }) {
return `${domain} wants you to sign in with your Ethereum account:
${address}
${statement}
URI: ${uri}
Version: ${version}
Chain ID: ${chainId}
Nonce: ${nonce}
Issued At: ${issuedAt}
Expiration Time: ${expirationTime}`;
}
// Fetch nonce for SIWE login
async function getNonce(address) {
const url = `${SOLVER_BASE_URL}/nonce/${address}`;
const { data } = await axios.get(url);
return data.nonce;
}
// Fetch the asset price from Muon, convert the returned wei value to a standard decimal string.
async function fetchMuonPriceConverted() {
try {
const MUON_URL = ""; //This is the uPnL_A_WithSymbolPrice Muon API request.
const response = await axios.get(MUON_URL);
const fetchedPriceWei = response.data.result.data.result.price;
if (!fetchedPriceWei) {
throw new Error("Muon price not found in response.");
}
// Convert the wei value to a human-readable decimal (assumes 18 decimals)
const priceDecimal = ethers.formatUnits(fetchedPriceWei, 18);
return priceDecimal;
} catch (error) {
console.error("Error fetching Muon price:", error.response?.data || error.message);
throw error;
}
}
// Call the /instant_close endpoint to close a position.
// The endpoint expects an object with:
// {
// "quote_id": number,
// "quantity_to_close": "string",
// "close_price": "string"
// }
async function closeInstantPosition(token, quote_id, quantity_to_close, close_price) {
const payload = {
quote_id, // Example: 23688
quantity_to_close, // Example: "6.1"
close_price, // Example: "2.3"
};
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
};
const response = await axios.post(`${SOLVER_BASE_URL}/instant_close`, payload, { headers });
return response.data;
}
// --------------------------------------------------------------------
// Main Flow: SIWE Login then Instant Close Trade
// --------------------------------------------------------------------
(async function main() {
try {
console.log(`\n[1/4] Wallet Address: ${wallet.address}`);
const nonce = await getNonce(activeAccount);
console.log(`[2/4] Got nonce: ${nonce}`);
// Build the SIWE message.
const siweMessage = buildSiweMessage({
domain: DOMAIN,
address: wallet.address,
statement: `msg: ${activeAccount}`,
uri: LOGIN_URI,
version: "1",
chainId: CHAIN_ID,
nonce,
issuedAt: ISSUED_AT,
expirationTime: EXPIRATION_DATE,
});
console.log("\n[3/4] SIWE message to sign:\n", siweMessage);
// Sign the SIWE message.
const signature = await wallet.signMessage(siweMessage);
console.log("\nSignature:", signature);
// Build the login request body.
const loginBody = {
account_address: activeAccount,
expiration_time: EXPIRATION_DATE,
issued_at: ISSUED_AT,
signature,
nonce,
};
const loginHeaders = {
"Content-Type": "application/json",
Origin: ORIGIN,
Referer: ORIGIN,
};
console.log("\n[4/4] Sending login request...");
const loginResponse = await axios.post(`${SOLVER_BASE_URL}/login`, loginBody, { headers: loginHeaders });
console.log("Login response:", loginResponse.data);
// Extract the access token.
const token = loginResponse.data.access_token;
if (!token) {
throw new Error("No access token received from login.");
}
// Fetch and log the Muon price in standard decimal format.
const muonPrice = await fetchMuonPriceConverted();
console.log("Fetched Muon Price (converted):", muonPrice);
// ----------------------------------------------------------------
// Instant Close Parameters – Replace these with your actual values:
const quote_id = 24718; // The quote ID (number) for the position to close.
const quantity_to_close = "6.1"; // The quantity to close as a string.
const close_price = (muonPrice * 0.95).toString(); //Position was LONG, so we're going to decrease a little to account for hedger spread.
console.log("closePrice: ", close_price);
console.log("\nSending instant close request...");
const closeResponse = await closeInstantPosition(token, quote_id, quantity_to_close, close_price);
console.log("Instant close response:", closeResponse);
} catch (err) {
console.error("Error in SIWE login flow or closing position:", err.response?.data || err.message);
}
})();Explanation
Last updated
