Instant Login (EOA)
Instant Login for Instant Open/Close
Before you can perform instant open or instant close actions, your sub‑account must delegate access to the solver (Party B) for these functions. Once delegated, you must obtain an access token through a two-step SIWE (Sign-In With Ethereum) login flow. This process ensures that you are authorized to execute instant actions and that your request includes a validated expiration date.
Flow Overview
Delegate Access: First, delegate access from your sub‑account to the solver’s address so that they can call functions (such as open, request to close, or cancel close) on your behalf.
Fetch a Nonce: Query the hedger’s nonce endpoint using your sub‑account address. The nonce ensures the uniqueness of your SIWE message and prevents replay attacks.
Build and Sign the SIWE Message: Construct a SIWE message that includes your wallet address, the nonce, a statement (the sub-account), and timestamps (issued and expiration). This message must be signed by your wallet to confirm your intent.
Send Login Request: Submit your signed SIWE message to the hedger’s
/login
endpoint. Upon successful verification, you will receive an access token. This token is then used for subsequent instant open or close operations.
Delegating Access
To enable instant actions on the frontend, users must delegate specific functions to solvers so they can perform actions on the user's behalf without requiring signatures for each request.
Front-ends should ensure that the following functions are delegated to the solver addresses through the delegateAccesses()
method on the MultiAccount contract, passing these function signatures.:
sendQuote
: 0x7f2755b2sendQuoteWithAffiliate
: 0x40f1310crequestToClosePosition
: 0x501e891frequestToCancelQuote
: 0xa8ffc7abrequestToCancelCloseRequest
: 0xa63b9363allocate
: 0x90ca796b
After Delegating Access you can proceed with the login process.
Example Script
Below is a Node.js script (using ethers and axios) that demonstrates the nonce fetch and instant login flow:
// Load environment variables from a .env file
// activeAccount is the sub-account
require("dotenv").config();
// Import required libraries: ethers for Ethereum utilities, axios for HTTP requests, and siwe for Sign-In With Ethereum
const { ethers } = require("ethers");
const axios = require("axios");
const { SiweMessage } = require("siwe");
// Retrieve the private key from environment variables
const PRIVATE_KEY = process.env.PRIVATE_KEY;
// Convert the active account address from the env into a checksummed Ethereum address.
// Using ethers.getAddress ensures the address format is correct. This is the sub-account.
const activeAccount = ethers.getAddress(process.env.activeAccount);
const wallet = new ethers.Wallet(PRIVATE_KEY);
// Define the base URL of the authentication (solver) server. In this example we'll use Perps Hub on Arbitrum
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();
const EXPIRATION_DATE = new Date(Date.now() + (24 * 60 * 60 * 10 * 1000)).toISOString(); //Longer expiration
//Fetching Nonce from Solver
async function getNonce(address) {
const url = `${SOLVER_BASE_URL}/nonce/${address}`;
const { data } = await axios.get(url);
return data.nonce;
}
// -----------------------------
// MAIN EXECUTION BLOCK
// -----------------------------
(async function main() {
try {
console.log(`\n[1/4] Wallet Address: ${wallet.address}`);
console.log(`[1.5/4] Active Account (Checksum): ${activeAccount}`);
// Fetch the nonce for the active account from the server
const nonce = await getNonce(activeAccount);
console.log(`[2/4] Got nonce: ${nonce}`);
// Create the SIWE message using the SIWE library
const siweMessage = new SiweMessage({
domain: DOMAIN,
address: wallet.address,
statement: `msg: ${activeAccount}`,
uri: LOGIN_URI,
version: "1",
chainId: CHAIN_ID,
nonce: nonce,
issuedAt: ISSUED_AT,
expirationTime: EXPIRATION_DATE,
});
// Convert the SIWE message object to string format for signing
const messageToSign = siweMessage.prepareMessage();
console.log("\n[3/4] SIWE message to sign:\n", messageToSign);
// Sign the SIWE message using the wallet's private key
const signature = await wallet.signMessage(messageToSign);
console.log("\nSignature:", signature);
// Construct the request body to send to the login endpoint, including the signature and nonce
const body = {
account_address: activeAccount,
expiration_time: EXPIRATION_DATE,
issued_at: ISSUED_AT,
signature,
nonce
};
console.log("body: ", body);
// Define HTTP headers including content type and origin (used for CORS validation on the server)
const headers = {
"Content-Type": "application/json",
Origin: ORIGIN,
Referer: ORIGIN,
};
console.log("\n[4/4] Sending login request...");
// Send the login request (POST) to the server with the constructed body and headers
const response = await axios.post(
`${SOLVER_BASE_URL}/login`,
body,
{ headers }
);
console.log("Login response:", response.data);
} catch (err) {
console.error("Error in SIWE login flow:", err.response?.data || err.message);
}
})();
Upon successful login, the response includes an access token. This token must be attached in the headers of any instant open or close requests that you subsequently send.
Last updated