Transfer simulation
Simulate a token transfer before you submit it, and get an authoritative will-clear or will-revert verdict plus a classified list of compliance blockers.
The transfer simulation endpoint runs a proposed token transfer against the on-chain compliance engine and returns whether it will-clear or will-revert. When it will revert, the response lists each compliance blocker, attributes it to the sender or recipient, and classifies how to resolve it. An integration uses this to turn an opaque on-chain compliance revert into a clear explanation before it ever submits a transaction.
Use this page for the request shape, the verdict semantics, the blocker and remediation fields, and the permission boundary that decides how much detail a caller receives. The endpoint is a read: it never changes state and never moves a token.
When to use simulation
Call this endpoint when you have a concrete transfer in mind, a sender, a recipient, and an amount, and you want to know the outcome before signing. The Console uses it this way in two places: the investor transfer form checks the verdict at the confirm step, and the operator pre-check console runs a transfer to explain why it would fail and who can fix it.
The endpoint complements the lighter recipient eligibility check. Eligibility answers whether one address is a registered recipient for an operation. Simulation answers whether a full transfer of a specific amount between two specific parties clears every active compliance module right now.
| Decision | Use transfer simulation for | Check elsewhere |
|---|---|---|
| Will this exact transfer clear? | An authoritative will-clear or will-revert verdict for the full transfer. | Whether the recipient is even a registered identity: recipient eligibility. |
| Why would it revert, and who can fix it? | A classified blocker list with per-blocker remediation. | The catalog of module-backed controls: compliance modules. |
| Can I rely on the verdict at execution? | A pre-flight signal that matches the contract's own check at probe time. | The final authority, which the contract still enforces when the transaction runs. |
Simulate a transfer
Send the token address in the path and the from, to, and amount in the query string. Express amount as a stringified integer in the token's smallest unit.
curl "https://your-platform.example.com/api/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/transfer-simulate?from=0x71C7656EC7ab88b098defB751B7401B5f6d8976F&to=0xabc0000000000000000000000000000000000001&amount=1000000000000000000" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"When the transfer clears, the verdict is will-clear and the blocker list is empty:
{
"data": {
"verdict": "will-clear",
"blockers": []
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/transfer-simulate"
}
}When the transfer would revert, the verdict is will-revert and the blocker list explains why. Each blocker names the failing module, the party it applies to, the failure reason, and how to resolve it:
{
"data": {
"verdict": "will-revert",
"blockers": [
{
"code": "identity-not-verified:transfer-approval",
"moduleTypeId": "transfer-approval",
"moduleInstance": "0xabc0000000000000000000000000000000000099",
"party": "recipient",
"reason": "identity-not-verified",
"remediationClass": "self-serve",
"remediation": {
"kind": "issue-claim",
"topics": ["kyc"]
},
"topics": ["kyc"]
}
]
},
"links": {
"self": "/v2/tokens/0x71C7656EC7ab88b098defB751B7401B5f6d8976F/transfer-simulate"
}
}Parameters
| Field | Type | Notes |
|---|---|---|
tokenAddress | path string | The token contract address to simulate the transfer against. |
from | query string | The sender address for the simulated transfer. |
to | query string | The recipient address for the simulated transfer. |
amount | query string | The token amount to simulate, as a stringified integer in the token's smallest unit. |
Response fields
| Field | Type | Notes |
|---|---|---|
verdict | string | will-clear when the transfer passes, will-revert when it does not. Always sourced from the on-chain check. |
blockers | array | Empty when verdict is will-clear. At least one entry when verdict is will-revert. |
code | string | Machine-readable blocker code. Carries the module's revert selector, or a classifier code such as unrecognized-rule when no module matched. |
moduleTypeId | string | The type of compliance module that raised the blocker. |
moduleInstance | string | The on-chain address of the specific module instance that raised the blocker. |
party | string | The transfer party the blocker applies to: sender or recipient. |
reason | string | Why the module vetoed the transfer: identity-not-registered, identity-not-verified, or module-blocked. |
remediationClass | string | Who resolves the blocker: self-serve, operator, or structural. |
remediation | object | The path to resolve the blocker. One of issue-claim, operator-action, or none. |
topics | array | Claim topics relevant to the blocker. Populated when the failure reason is claim-related. |
What the verdict means
The verdict comes from exactly one on-chain canTransfer(from, to, amount) call against the token's compliance engine. Nothing else changes it. If the on-chain call succeeds, the verdict is will-clear. If it reverts, the verdict is will-revert. The blocker list is informational: it explains a revert but never overrides the verdict.
A few boundary cases follow from that rule:
- A token with no compliance engine wired returns
will-clearwith an empty blocker list, because there is no on-chain control to apply. - A
will-revertresponse always carries at least one blocker. When the contract reverts but no specific module can be attributed, the response returns a singleunrecognized-ruleblocker rather than an empty list. - If the compliance engine cannot be reached, the endpoint returns a
503service-unavailable error instead of a verdict. The endpoint never reportswill-clearwhen it could not run the check. Treat the error as a transient condition and retry.
How blockers classify remediation
Each blocker tells the integration not just what failed but how to fix it. The reason names the failure class, the remediationClass names who can act, and the remediation object names the concrete path.
reason: identity-not-registeredmeans the party has no identity entry in the registry.reason: identity-not-verifiedmeans an identity exists but a required claim is missing or expired.reason: module-blockedmeans a module actively blocks the address, such as a freeze, a blocklist entry, or a country bar.
The remediation object discriminates on kind:
issue-claimcarries the claimtopicsthe party must satisfy. This usually pairs with aself-serveclass: the investor acquires the required claims and the transfer clears.operator-actionnames the administrative step that unblocks the transfer:unfreeze,remove-blocklist, orregister-identity. This pairs with anoperatorclass, because a platform operator must act.nonemeans no standard workflow resolves the blocker. This pairs with astructuralclass.
This is what lets a transfer form route a user correctly: a self-serve blocker prompts the investor to complete verification, while an operator blocker tells them to contact an administrator.
Detail is gated by permission
The verdict is always returned, but the blocker detail is not. The endpoint enumerates per-address registration, claim, and module facts only for callers that hold a compliance-detail role, the same role set that controls who can open the operator pre-check console. This stops the endpoint from becoming a compliance-enumeration oracle over arbitrary addresses.
A caller without that role still gets the authoritative verdict. On a revert, that caller receives a single generic compliance-restricted blocker that carries the will-revert result without disclosing which rule, identity, or claim caused it. The response is never empty on a revert, and it never leaks detail to an unprivileged caller.
The simulation does not replace the on-chain control
The verdict is a pre-flight signal, not the final authority. The token contract still enforces compliance when the transaction executes. A will-clear verdict reflects the compliance state at the moment of the probe; state can change before the transfer is signed and mined. Always handle an on-chain revert even after a will-clear simulation, and surface the contract's compliance error to the operator when one occurs. For the error model, see Error handling.
Related reading
- Recipient eligibility check for the lighter single-address pre-check that the transfer form runs first.
- Compliance modules for the module-backed transfer controls the on-chain contract enforces.
- Identity verification for how a party becomes a registered, verified identity in the first place.
- API reference for the generated OpenAPI contract and a typed client for this endpoint.