// --- Step 2: Setup ---
const provider = window.ethereum
const ondoBase = 'api.ondoperps-sandbox.xyz'
const ondoUrl = `https://${ondoBase}`
const testMarket = 'XAU'
const parseResponse = (data) => {
if (!data.success) {
throw new Error(`Request failed: ${JSON.stringify(data)} (code: ${data.error_code})`)
}
return data.result
}
const fetchPost = async (endpoint, data) => {
return fetch(`${ondoUrl}${endpoint}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => parseResponse(data))
}
let jwtToken = ''
const fetchAuthPost = async (endpoint, data) => {
return fetch(`${ondoUrl}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`,
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => parseResponse(data))
}
const fetchAuthGet = async (endpoint) => {
return fetch(`${ondoUrl}${endpoint}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${jwtToken}`,
},
})
.then(response => response.json())
.then(data => parseResponse(data))
}
// --- Step 3: Authenticate (SIWE) ---
let chainId = await ethereum.request({ method: 'eth_chainId' })
if (chainId !== '0x1') {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x1' }],
})
}
const accounts = await provider.request({ method: 'eth_requestAccounts' })
const from = accounts[0]
const siweSign = async (siweMessage) => {
const bytes = new TextEncoder().encode(siweMessage)
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('')
const msg = `0x${hex}`
return await provider.request({
method: 'personal_sign',
params: [msg, from],
})
}
const getChallengeData = {
walletAddress: from,
chainId: '1'
}
const challenge = await fetchPost('/v1/auth/erc-4361/login/get_challenge', getChallengeData)
const signedMessage = await siweSign(challenge.message)
const completeChallengeData = {
id: challenge.id,
signature: signedMessage,
}
const completeChallenge = await fetchPost('/v1/auth/erc-4361/login/complete_challenge', completeChallengeData)
jwtToken = completeChallenge.token
// Accept terms (first login only)
await fetchAuthPost('/v1/agreement', {
termsVersion: 1,
privacyVersion: 1,
})
// --- Step 4: Deposit ---
// Get your account ID
const accountData = await fetchAuthGet('/v1/account')
const accountID = accountData.accountID
// Request a deposit address
const provisionAddressReq = {
symbol: 'USDC',
deposit_destination: {
id: accountID,
wallet: 'margin',
},
network: 'ethereum', // also supports: solana, avalanche
}
const provisioned = await fetchAuthPost('/v1/provision_address', provisionAddressReq)
const depositAddress = provisioned.address
// Switch to the deposit chain (Sepolia for testnet, mainnet for production)
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0xAA36A7' }], // Sepolia testnet
})
// Send ERC-20 transfer: 50 USDC to the provisioned deposit address
// 0xa9059cbb = transfer(address,uint256) selector
// 0x02faf080 = 50,000,000 (50 USDC with 6 decimals)
const depositNoPrefix = depositAddress.replace(/^0x/i, '')
const sent_tx = await window.ethereum.request({
method: 'eth_sendTransaction',
params: [{
to: '0x94a9D9AC8a22534E3FaCa9F4e7F2E2cf85d5E4C8', // Sepolia USDC contract
from: from,
data: `0xa9059cbb000000000000000000000000${depositNoPrefix}0000000000000000000000000000000000000000000000000000000002faf080`,
}],
})
// Poll for transaction confirmation
for (;;) {
const tx = await window.ethereum.request({
method: 'eth_getTransactionByHash',
params: [sent_tx],
})
if (tx.blockHash && tx.blockHash !== '') {
break
}
// Wait 2 seconds between polls to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 2000))
}
console.log('Deposit confirmed on-chain')
// --- Step 5: Place Orders ---
const marketOrderReq = {
market: `${testMarket}-USD.P`,
type: 'market',
side: 'sell',
size: '0.01',
}
const placedMarketOrder = await fetchAuthPost('/v1/perps/orders', marketOrderReq)
const orderDetails = await fetchAuthGet(`/v1/perps/orders/${placedMarketOrder.orderId}`)
const positions = await fetchAuthGet('/v1/perps/positions')
// Uncomment to place a limit order:
// const limitOrderReq = {
// market: `${testMarket}-USD.P`,
// type: 'limit',
// side: 'buy',
// price: '5280',
// size: '0.02',
// }
// const placedLimitOrder = await fetchAuthPost('/v1/perps/orders', limitOrderReq)
// --- Step 6: Charting Data ---
const fetchHistory = async (endpoint) => {
return fetch(`${ondoUrl}${endpoint}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}).then(response => response.json())
}
const ts = Math.floor(Date.now() / 1000)
const history = await fetchHistory(
`/v1/perps/history?symbol=${testMarket}USD.P&resolution=15&to=${ts}&countback=2`
)