Oracle
Sync Oracle Price
Push a NAV price on-chain
POST
/
lending-external-securities-v2
/
api
/
markets
/
{marketId}
/
sync-oracle
curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/{marketId}/sync-oracle" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{"price": 105.40}'
const url = `https://api.trusset.org/lending-external-securities-v2/api/markets/${marketId}/sync-oracle`;
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify({ price: 105.40 }) });
const { success, data, error } = await res.json();
if (!success) {
if (error.code === 'PRICE_DEVIATION_TOO_LARGE') await notifyOperator(error.message);
throw new Error(error.code);
}
const tx = await priceSignerWallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ price: 105.40, txHash: tx.hash })
});
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePriceWithSignature",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x9c1e57a2d0b34f86e7a15c9d2b4f60e38a7d4b70",
"attestationId": "clx4k2p9e0001m8tq3v7h5z2a"
}
}
{
"success": true,
"data": {
"previousPrice": "104.82",
"newPrice": "105.4",
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "100.0",
"newPrice": "96.5",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"markDown": true,
"markDownOnly": true,
"description": "Mark down the fixed price from 100.0 to 96.5 USDC"
}
}
{
"success": false,
"error": {
"code": "FIXED_PRICE_ACTIVE",
"message": "This market has a fixed price of 100.0 USDC, set at deployment. It can only be marked down: send a price below 100.0 USDC.",
"details": { "currentPrice": "100.0", "markDownOnly": true }
}
}
{
"success": false,
"error": {
"code": "SIGNER_NOT_AUTHORIZED",
"message": "Wallet 0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52 is not authorized on oracle 0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423. The oracle owner must call setAuthorizedSigner, or the declared price signer can attest the price and anyone may then submit that attestation."
}
}
{
"success": false,
"error": {
"code": "PRICE_DEVIATION_TOO_LARGE",
"message": "Price deviation exceeds circuit breaker. On-chain: 104.82 USDC, new: 250 USDC. Max allowed: 50%",
"currentPrice": "104.82",
"maxDeviationPercent": 50
}
}
{
"success": false,
"error": {
"code": "EXTERNAL_FEED_ACTIVE",
"message": "This oracle prices from the external feed 0x6e2f41c8a93b07d5e1f4a2c6b8d03e97f5a1c42d and refuses attested updates. There is nothing to sign or sync: the market reads the feed directly."
}
}
{
"success": false,
"error": {
"code": "MISSING_PRICE",
"message": "A positive NAV price is required"
}
}
Writes a new price to the market’s oracle. This is how a
Price authority does not have to be your instance’s primary wallet. When the lender of record took the role it declared a
The deviation bound itself is set in the Issuer Portal and is not on this API. On a
Confirming does not return the typed transaction verification codes. A receipt that is missing, reverted, sent to another contract or calling another function answers
NAV- or MARKET-priced market moves its stored price: the market’s price authority publishes a value and pushes it here. Between pushes, the price moves only when a transaction carries a signed price.
An ORACLE-priced market reads an external feed instead, and its oracle refuses every attested price. This endpoint refuses it with EXTERNAL_FEED_ACTIVE.
A FIXED market holds the price set at deployment for its whole life, and it never goes stale. This endpoint can only mark that price down. See Mark down a fixed price.
Like every other write on this surface, nothing is submitted for you. The call returns the unsigned transaction, you broadcast it, then confirm it with txHash. price is required on both calls.
updatePrice is authorized against the wallet that sends it, which must be an authorized signer on the oracle or its owner. The API checks the resolved signer before building the calldata and refuses with SIGNER_NOT_AUTHORIZED if it is not authorized. Broadcast from the wallet named in signer. Check signerAuthorized and priceSignerAuthorized on Get Oracle Status first.priceSigner, and that address is what this endpoint resolves to when you name none. Pass signerAddress to check against a different key.
Path Parameters
string
required
Body Parameters
number
required
Net asset value per collateral token, denominated in the market’s borrow asset. Must be greater than zero, and a numeric string is accepted too. Required on the confirm call as well, where the value recorded comes from the mined transaction instead. More decimal places than the borrow asset carries fail with
SYNC_FAILED.string
Wallet the authorization check runs against. Omit it and the market’s declared
priceSigner is used. When the market declares none, the instance’s first verified wallet is. A value that is not a 0x-prefixed 40-character hex address is ignored. Not used when confirming with txHash, or when a recorded attestation is submitted.string
Hash of the mined transaction, sent together with
price to confirm it and record the push. Omit it to receive the calldata. null or an empty string counts as omitted. Any other value that is not a 0x-prefixed 64-character hex string is refused with VALIDATION_ERROR, and nothing is built or recorded.Response Fields
object
Show child attributes
Show child attributes
boolean
Nested inside
data on the calldata response.string
SIGN_TRANSACTION. Returned when txHash is omitted.object
Unsigned transaction,
{ to, data, value, chainId }, targeting the oracle. Returned when txHash is omitted.string
updatePrice, or updatePriceWithSignature when a recorded attestation is carried. Returned when txHash is omitted.string
The wallet the authorization check ran against, or the key that signed the carried attestation. Returned when
txHash is omitted.string
The recorded attestation being submitted. Present only with
updatePriceWithSignature.string
The on-chain price before this write. When confirming, read from the oracle’s
PriceUpdated event in the receipt, and null only when the receipt carries none.string
The price to be written, as sent. Read from the mined transaction when confirming.
boolean
true on a FIXED market, where the write lowers the fixed price. Absent otherwise.boolean
true on a FIXED market. Absent otherwise.string
On a
FIXED market, the mark-down in words, naming the old and the new price. Absent otherwise.string
Transaction hash. Returned when confirming with
txHash.Submit a Recorded Attestation
A price signer that keeps its key outside this instance can sign the payload from Sign Price and post the signature to Record Price Attestation. A later push of exactly that price returnsupdatePriceWithSignature carrying the attestation. The oracle authorizes the key that signed, so any wallet may broadcast it and no authorization check runs on the sender.
The attestation is used only while it is unexpired, not yet pushed, and recorded through this instance. It must also be dated after the oracle’s current price, be at most 15 minutes old, and still pass the oracle’s own signature check. Otherwise the call falls back to a plain updatePrice. An attested price is dated at its signed timestamp, so its age runs from signing, not from broadcast.
Mark down a fixed price
On aFIXED market the oracle owner or an authorized price signer can lower the price, and nothing can raise it again through a price update. price must be below the current fixed price. An equal or higher price is refused with 409 FIXED_PRICE_ACTIVE, and details carries currentPrice and markDownOnly: true. A mark-down is bounded by the circuit breaker below, and no recorded attestation is ever carried, because the oracle accepts no signed price on a fixed market.
A mark-down is final. The market lends and liquidates against the lower price as soon as the transaction mines, and no price update can restore the old one. Mark down only on a decision the lender of record stands behind, and push one step at a time when the move is large.
FIXED market the Issuer Portal only tightens it, because it is the only limit on how far one mark-down, or the mark-downs of one hour, can move the price. A wider bound is refused there with 409 FIXED_PRICE_BOUND_LOCKED, and with 503 ORACLE_STATE_UNAVAILABLE when the bound in force cannot be read. An oracle on the current implementation refuses a wider bound sent to it directly as well, reverting FixedPriceActive. A fixed oracle created before the factory carried that implementation runs the one it was created with until its owner upgrades it, and until then only the Issuer Portal holds the bound. Read the bound in force as maxDeviationBps on Get Oracle Status before you rely on it.
When whether the oracle holds a fixed price cannot be read, the call answers 503 PRICE_STATE_UNAVAILABLE and prepares nothing.
Circuit Breaker
The oracle rejects a price that deviates from the current price by more thanmaxDeviationBps, defaulting to 5000 basis points, which is 50 percent. This guards against a fat-fingered NAV wiping out healthy loans in one transaction. The same limit also bounds the total movement within a rolling one-hour window, measured from the price that opened the window.
Every push-mode oracle is initialized with a positive price at deployment, so even the first push is measured against it. The API runs both checks before building calldata. A step too large is refused with PRICE_DEVIATION_TOO_LARGE, and a push that would take the hour’s movement past the limit with PRICE_WINDOW_DEVIATION_TOO_LARGE.
Both refusals carry currentPrice and maxDeviationPercent on the error object. The window refusal adds windowAnchorPrice, windowStartedAt, windowResetsAt, acceptedLow and acceptedHigh. On a FIXED market they also carry details.markDownOnly: true.
A genuine repricing beyond the limit requires the oracle owner to widen maxDeviationBps, or a sequence of smaller steps. Steps that land within one hour count against the same window. Do not retry a rejected push unchanged. Surface the deviation to an operator, because a retry sends the same price into the same breaker.
A confirmed push is recorded on the market as an
ORACLE_SYNC transaction, visible through List Transactions, and updates lastOracleUpdate on the market record. A push that is broadcast but never confirmed still appears in the pushed list on Get Oracle History, which mirrors the oracle’s events.curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/{marketId}/sync-oracle" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{"price": 105.40}'
const url = `https://api.trusset.org/lending-external-securities-v2/api/markets/${marketId}/sync-oracle`;
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify({ price: 105.40 }) });
const { success, data, error } = await res.json();
if (!success) {
if (error.code === 'PRICE_DEVIATION_TOO_LARGE') await notifyOperator(error.message);
throw new Error(error.code);
}
const tx = await priceSignerWallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ price: 105.40, txHash: tx.hash })
});
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePriceWithSignature",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x9c1e57a2d0b34f86e7a15c9d2b4f60e38a7d4b70",
"attestationId": "clx4k2p9e0001m8tq3v7h5z2a"
}
}
{
"success": true,
"data": {
"previousPrice": "104.82",
"newPrice": "105.4",
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "100.0",
"newPrice": "96.5",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"markDown": true,
"markDownOnly": true,
"description": "Mark down the fixed price from 100.0 to 96.5 USDC"
}
}
{
"success": false,
"error": {
"code": "FIXED_PRICE_ACTIVE",
"message": "This market has a fixed price of 100.0 USDC, set at deployment. It can only be marked down: send a price below 100.0 USDC.",
"details": { "currentPrice": "100.0", "markDownOnly": true }
}
}
{
"success": false,
"error": {
"code": "SIGNER_NOT_AUTHORIZED",
"message": "Wallet 0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52 is not authorized on oracle 0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423. The oracle owner must call setAuthorizedSigner, or the declared price signer can attest the price and anyone may then submit that attestation."
}
}
{
"success": false,
"error": {
"code": "PRICE_DEVIATION_TOO_LARGE",
"message": "Price deviation exceeds circuit breaker. On-chain: 104.82 USDC, new: 250 USDC. Max allowed: 50%",
"currentPrice": "104.82",
"maxDeviationPercent": 50
}
}
{
"success": false,
"error": {
"code": "EXTERNAL_FEED_ACTIVE",
"message": "This oracle prices from the external feed 0x6e2f41c8a93b07d5e1f4a2c6b8d03e97f5a1c42d and refuses attested updates. There is nothing to sign or sync: the market reads the feed directly."
}
}
{
"success": false,
"error": {
"code": "MISSING_PRICE",
"message": "A positive NAV price is required"
}
}
Error Codes
| Code | HTTP | Cause |
|---|---|---|
MISSING_PRICE | 400 | price absent, non-numeric, or not greater than zero, on the calldata call and the confirm call alike |
NO_ORACLE | 400 | The market has no oracle recorded |
EXTERNAL_FEED_ACTIVE | 400 | The market’s oracle reads an external feed and refuses pushed prices |
WALLET_NOT_CONFIGURED | 400 | No signerAddress was given, the market declared no price signer, this instance has no verified wallet, and no recorded attestation matched |
PRICE_DEVIATION_TOO_LARGE | 400 | The step from the on-chain price exceeds maxDeviationBps |
PRICE_WINDOW_DEVIATION_TOO_LARGE | 400 | The movement within the current one-hour window would exceed maxDeviationBps. The error names the accepted band |
SYNC_FAILED | 400 | The update could not be prepared, or the confirm call failed verification. message carries the reason |
VALIDATION_ERROR | 400 | txHash is present but not a 0x-prefixed 64-character hex string. details names the field |
MISSING_MARKET_ID | 400 | marketId is longer than 100 characters |
SIGNER_NOT_AUTHORIZED | 403 | The resolved signer is neither an authorized signer nor the owner. Checked before the calldata is built; skipped when a recorded attestation is carried and when confirming |
MARKET_NOT_FOUND | 404 | No market with this ID on your instance |
FIXED_PRICE_ACTIVE | 409 | The market has a fixed price and price is not below it. details carries currentPrice and markDownOnly: true. Send a lower price |
PRICE_STATE_UNAVAILABLE | 503 | Whether the oracle holds a fixed price could not be read, so nothing was prepared. Retry shortly |
SYNC_FAILED, with the verification message such as Transaction not found or not yet mined.⌘I
curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/{marketId}/sync-oracle" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{"price": 105.40}'
const url = `https://api.trusset.org/lending-external-securities-v2/api/markets/${marketId}/sync-oracle`;
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify({ price: 105.40 }) });
const { success, data, error } = await res.json();
if (!success) {
if (error.code === 'PRICE_DEVIATION_TOO_LARGE') await notifyOperator(error.message);
throw new Error(error.code);
}
const tx = await priceSignerWallet.sendTransaction(data.transaction);
await tx.wait();
await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify({ price: 105.40, txHash: tx.hash })
});
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePriceWithSignature",
"previousPrice": "104.82",
"newPrice": "105.4",
"signer": "0x9c1e57a2d0b34f86e7a15c9d2b4f60e38a7d4b70",
"attestationId": "clx4k2p9e0001m8tq3v7h5z2a"
}
}
{
"success": true,
"data": {
"previousPrice": "104.82",
"newPrice": "105.4",
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b"
}
}
{
"success": true,
"data": {
"success": true,
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"data": "0x...",
"chainId": 11155111,
"value": "0"
},
"functionName": "updatePrice",
"previousPrice": "100.0",
"newPrice": "96.5",
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"markDown": true,
"markDownOnly": true,
"description": "Mark down the fixed price from 100.0 to 96.5 USDC"
}
}
{
"success": false,
"error": {
"code": "FIXED_PRICE_ACTIVE",
"message": "This market has a fixed price of 100.0 USDC, set at deployment. It can only be marked down: send a price below 100.0 USDC.",
"details": { "currentPrice": "100.0", "markDownOnly": true }
}
}
{
"success": false,
"error": {
"code": "SIGNER_NOT_AUTHORIZED",
"message": "Wallet 0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52 is not authorized on oracle 0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423. The oracle owner must call setAuthorizedSigner, or the declared price signer can attest the price and anyone may then submit that attestation."
}
}
{
"success": false,
"error": {
"code": "PRICE_DEVIATION_TOO_LARGE",
"message": "Price deviation exceeds circuit breaker. On-chain: 104.82 USDC, new: 250 USDC. Max allowed: 50%",
"currentPrice": "104.82",
"maxDeviationPercent": 50
}
}
{
"success": false,
"error": {
"code": "EXTERNAL_FEED_ACTIVE",
"message": "This oracle prices from the external feed 0x6e2f41c8a93b07d5e1f4a2c6b8d03e97f5a1c42d and refuses attested updates. There is nothing to sign or sync: the market reads the feed directly."
}
}
{
"success": false,
"error": {
"code": "MISSING_PRICE",
"message": "A positive NAV price is required"
}
}
