The validate endpoint ensures that the unsigned transaction you’re about to sign matches exactly what was simulated. Each field is checked independently, so you know precisely what changed if validation fails.
POST https://shield.api.enso.build/api/v1/validate
Request Parameters
| Field | Type | Required | Description |
|---|
simulationId | string (UUID) | Yes | The simulationId from a prior /simulate call |
transaction.data | string | Yes | Hex-encoded calldata |
transaction.to | string | Yes | Target contract address |
transaction.from | string | Yes | Sender address |
transaction.value | string | Yes | Native token value in wei |
transaction.chainId | number | Yes | Chain ID |
Response
| Field | Type | Description |
|---|
valid | boolean | true only when all checks pass |
simulationId | string | The simulation ID validated against |
checks.chainId | boolean | Chain ID matches |
checks.data | boolean | Calldata hash matches |
checks.to | boolean | Target contract address matches |
checks.from | boolean | Sender address matches |
checks.value | boolean | Native token value matches |
Examples
Successful Validation
curl -X POST https://shield.api.enso.build/api/v1/validate \
-H "Authorization: Bearer $ENSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"simulationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"transaction": {
"data": "0x3593564c...",
"to": "0x80EbA3855878739F4710233A8a19d89Bdd2ffB8E",
"from": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"value": "1000000000000000000",
"chainId": 1
}
}'
Response:
{
"valid": true,
"simulationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"checks": {
"chainId": true,
"data": true,
"to": true,
"value": true,
"from": true
}
}
Failed Validation (Tampered Data)
If the calldata has been modified since simulation, the data check will fail:
{
"valid": false,
"simulationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"checks": {
"chainId": true,
"data": false,
"to": true,
"value": true,
"from": true
}
}
Interpreting Failed Checks
| Check | When false | Likely Cause |
|---|
data | Calldata was modified | Transaction payload was tampered with, or a different function/arguments were encoded |
to | Target contract changed | The transaction is being sent to a different contract than simulated |
from | Sender changed | A different account is submitting the transaction |
value | ETH value changed | The native token amount was modified |
chainId | Wrong chain | The transaction is targeting a different chain than simulated |
If any check fails, do not sign the transaction. Re-simulate to get a fresh simulationId, or investigate why the transaction changed.
Cache Expiry
Simulations are cached for 5 minutes. If you call /validate after the simulation has expired, you’ll receive a 404 error:
{
"statusCode": 404,
"message": "Simulation a1b2c3d4-e5f6-7890-abcd-ef1234567890 not found or expired"
}
Re-simulate to get a fresh simulationId and try again.
Best Practices
- Validate immediately before signing — Don’t validate far in advance. Validate as close to the signing moment as possible within the 5-minute window.
- Log the simulationId — Store simulation IDs for debugging and audit trails.
- Re-simulate on failure — If validation fails, re-simulate to get a fresh result rather than retrying the same validation.
- Check individual fields — When
valid is false, inspect the checks object to understand exactly what changed.
async function safeSign(simulationId: string, tx: UnsignedTransaction) {
const validation = await validate(simulationId, tx);
if (!validation.valid) {
const failedChecks = Object.entries(validation.checks)
.filter(([, passed]) => !passed)
.map(([field]) => field);
throw new Error(`Validation failed for: ${failedChecks.join(", ")}`);
}
return await wallet.sendTransaction(tx);
}