Deployment
Deploy Market
Deploy a lending market for an imported security token
POST
/
lending-external-securities-v2
/
api
/
markets
/
deploy
curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/deploy" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"initialPrice": "104.82",
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"mode": "FREEZE",
"priceSource": "NAV",
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200,
"maxLoanDurationDays": 180
}'
const base = 'https://api.trusset.org/lending-external-securities-v2/api/markets/deploy';
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const build = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
collateralTokenAddress: '0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f',
initialPrice: '104.82',
candidates: [candidateAddress],
mode: 'CUSTODY',
borrowAssetAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F'
})
});
const { data: calldata } = await build.json();
const tx = await wallet.sendTransaction(calldata.transaction);
await tx.wait();
const confirm = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
txHash: tx.hash,
collateralTokenAddress: calldata.collateralTokenAddress
})
});
const { data: recorded } = await confirm.json();
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"data": "0x...",
"value": "0",
"chainId": 11155111
},
"functionName": "deployMarket",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAsset": {
"address": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"symbol": "USDC",
"decimals": 6
},
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"schemaVersion": "1.1.0",
"canonicalJson": "{\"market\":{\"borrowAsset\":\"0x98ad0ca091552e23c564b41c74282e5343d03e8a\",\"chainId\":11155111,\"collateralMode\":\"FREEZE\",\"factory\":\"0xf1ad12f5698f58badd8a662acffccb067a2ddb8a\",\"family\":\"FUNGIBLE\",\"priceSource\":\"NAV\"},\"profile\":{\"isin\":\"DE000A2YN900\",\"issuerName\":\"ACME Industrial AG\"},\"schemaVersion\":\"1.1.0\",\"token\":{\"decimals\":18,\"name\":\"ACME Industrial Bond 2031\",\"symbol\":\"ACME\",\"tokenAddress\":\"0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f\"}}",
"bundle": {
"schemaVersion": "1.1.0",
"market": {
"chainId": 11155111,
"factory": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"family": "FUNGIBLE",
"borrowAsset": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"collateralMode": "FREEZE",
"priceSource": "NAV"
},
"token": {
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"name": "ACME Industrial Bond 2031",
"symbol": "ACME",
"decimals": 18
},
"profile": {
"issuerName": "ACME Industrial AG",
"isin": "DE000A2YN900"
}
},
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"note": "The deployment signature attests this bundle: assetData(market).attested equals contentHash and attestor equals the signing wallet once the transaction mines."
},
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"seekingCurator": true,
"openToAnyCurator": false,
"rateOffering": {
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200
},
"termOffering": {
"maxLoanDurationSeconds": 15552000,
"termGracePeriodSeconds": 0,
"terminalDueDate": null,
"graceDefaultApplies": true
},
"operatorShareBps": 800,
"feeShares": {
"operatorBps": 800,
"distributorBps": 500,
"fixed": true,
"ofBase": "AFTER_INFRASTRUCTURE"
},
"alreadyExists": false,
"confirmWith": {
"endpoint": "POST /lending-external-securities-v2/api/markets/deploy",
"field": "txHash",
"required": ["txHash"],
"body": {
"txHash": null,
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"identityRegistry": null
},
"description": "Send the mined deployMarket transaction hash as txHash. Every other field is optional: the record is read from the deployment the receipt proves, and a supplied field is only cross-checked against it."
}
}
}
{
"success": true,
"data": {
"market": {
"id": "clx_secmarket_001",
"marketAddress": "0x70a0e25c7b768b87e658348b3b577678a173e038",
"oracleAddress": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"insuranceFundAddress": "0x8f1a6b30c7d24e95f0a3b81d6c47e2905fa3b9c2",
"adapterAddress": "0x4d2b93f70e18c6a5d34b027fe95c81a6730d77ae",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"collateralMode": "FREEZE",
"collateralDecimals": 18,
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"borrowAssetSymbol": "USDC",
"borrowAssetDecimals": 6,
"priceSource": "NAV",
"seekingCurator": true,
"curator": null,
"realizationMode": "UNDECLARED",
"operatorShare": "800",
"collateralFactor": "7500",
"liquidationThreshold": "8500",
"active": true
},
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b",
"relayerAddress": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"configurationStatus": { "fullyConfigured": false },
"collateralAuthStatus": {
"mode": "FREEZE",
"adapterAuthorized": false,
"interfaceSupported": true,
"requirement": "adapter_must_hold_agent_role"
},
"routerHolderAuthorized": false,
"oracleSignerAuthorized": false,
"liquidatorRoleGranted": false,
"pendingCurator": true,
"configurationSteps": [
{
"step": "grantAdapterAgentRole",
"description": "Grant agent role to freeze adapter on the security token",
"requiredRole": "TOKEN_AGENT_ADMIN",
"requiredBy": ""
}
],
"configurationCalldata": {
"grantAdapterAgentRole": {
"to": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"data": "0x...",
"description": "Grant agent role to freeze adapter on the security token",
"value": "0",
"chainId": 11155111
}
},
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"version": 1,
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"bundleAvailable": true
}
}
}
{
"success": false,
"error": {
"code": "OPERATOR_SHARE_FIXED",
"message": "operatorShareBps is no longer accepted: every market pays its operator a fixed 8 percent and its distributors a fixed 5 percent of the interest left after the 15 percent infrastructure fee, set by the contracts and read from the factory (OPERATOR_SHARE, DISTRIBUTOR_SHARE). Remove the field from the request."
}
}
{
"success": false,
"error": {
"code": "TOKEN_PROFILE_REQUIRED",
"message": "ACME has no published profile, so there is no asset data to attest and no market can be created against it. Open the token in Assets and fill in at least one field of its published profile, the overview this instance can always edit: the description, the issuer name, the sector, the website, the AMM contract or the reserve document (PUT /external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile from the issuer app, or PUT /external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile with an API key), then create the market: the issuer's own wallet attests that data inside the deployment signature. The instrument record with the identifiers, the jurisdiction and the offering documents is published by the token's controller only: by this instance when one of its verified wallets controls the token (PUT /instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f from the issuer app, or PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with an API key), otherwise through the API with the controller's signature (POST /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request, then PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with issuerApproval). A record already published for the token is part of the asset data either way.",
"details": {
"field": "profile",
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"imported": true,
"nextStep": "FILL_PROFILE",
"overviewFields": ["description", "issuerName", "sector", "website", "ammAddress", "reserveURI"],
"editProfile": "/external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"apiPaths": {
"editProfile": "/external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"instrumentApprovalRequest": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request"
}
}
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "rateCapBps",
"message": "A FIXED rate mode takes fixedRateBps only; rateFloorBps and rateCapBps belong to CLAMPED. Drop them or choose CLAMPED."
}
]
}
}
Deploys a complete market in one factory transaction: the lending module, its price oracle, interest rate model, insurance fund, collateral adapter, and a liquidation router dedicated to this market. The same transaction attests the market’s asset data. The hash of a canonical bundle built from the token’s profile travels inside the deployment, signed for by the deploying wallet.
The market is deployed seeking a lender of record. It names no admin, no issuer and no collateral agent. The deployment nominates
The three modes do not mix. Sending
Confirm by sending
Confirming with
A supplied
candidates, or sets openToAnyCurator to offer the market beyond them. The market stays inert until an institution takes the role through Accept Lender Role. See Lender of record.
Sign with the wallet named in signer, this instance’s registered wallet. The factory records the sending wallet as the market’s deployer and as the attestor of its asset data. Only that wallet or the factory owner can nominate candidates later.
The deployment sets the opening rate offering and term offering. After adoption, the issuer the lender of record names can change both. A new rate mode reaches open variable-rate loans from their next accrual, while a stamped fixed rate stays with the loan it was stamped on. A new term offering reaches new loans only. See Set Rate Mode and Set Term Config, and read the ranges and the factory’s own limits first with Get Deployment Options.
A collateral token can back several markets as long as they differ in collateral mode or in settlement token (stablecoin). A second market for the same token, mode and settlement asset returns 409 MARKET_EXISTS.
A freshly deployed market cannot take loans or liquidity. Two things are outstanding. A candidate must take the lender-of-record role, and the collateral token’s agent or identity registry operator must authorize the market’s adapter and router on the token. That party is typically neither Trusset nor the deployer. Call Get Setup Steps after deployment for the outstanding items and their calldata.
Body Parameters
string
required
Security token accepted as collateral. Must be imported into this instance with a published profile, and must not be the same token as the borrow asset. On the confirm call it is an optional cross-check.
CUSTODY mode needs only an ERC-20 surface (balanceOf and decimals), so ERC-20, ERC-1400 and ERC-3643 tokens all qualify. FREEZE mode also needs the ERC-3643 freeze surface (isAgent or getFrozenTokens). See Collateral token standards.A token whose ERC-1400 granularity() is above one base unit is refused in either mode with COLLATERAL_GRANULARITY_UNSUPPORTED. The market seizes the exact amount a liquidation or a term realization needs, which is almost never a whole multiple of the granularity, so the token would refuse the seizure.string
Opening collateral price as a decimal string, for example
"104.82". Must be greater than zero, with at most 18 decimal places. It is denominated in the borrow asset and parsed at its decimals. A market settling in USDC therefore accepts at most 6 decimal places here.Required for the NAV, MARKET and FIXED price sources. An ORACLE market takes its first price from the feed and ignores this field. On a FIXED market it is the price of one whole collateral token for the life of the market. See Fixed price.array
default:"[]"
Addresses nominated to become the market’s lender of record. Up to 20 entries, each a checksum-valid address that is not the zero address. Duplicates are dropped. Nominating stays possible after deployment through Nominate Candidate. An empty list is a market nobody can take yet, not a market nobody can ever take.
boolean
default:"false"
Offer the market to claimants beyond the named candidates. The factory admits a claimant nobody nominated when the borrower register the deployer installed verifies it. Through this API the claimant must also be an account Trusset approved as a lender of record. The claim takes two transactions, announced and then completed after a delay. See Accept Lender Role.An open
NAV or ORACLE market deployed with no identityRegistry is narrowed rather than refused. With no borrower register to admit them, claimants nobody nominated are refused, so only a nominee can take the market. An open MARKET market must install one.string
default:"FREEZE"
FREEZE or CUSTODY. See Collateral modes. Cannot be changed after deployment.string
default:"NAV"
NAV, MARKET, ORACLE or FIXED. Cannot be changed after deployment.NAV is a price the issuer publishes. MARKET signals an observable market price: the contract then refuses Dutch auctions and holds the auction floor premium at 98 percent or more. ORACLE reads the external feed named in oracleAddress and takes no pushed price at all. FIXED holds initialPrice for the life of the market, never goes stale, and can only be marked down. It is offered only when Get Deployment Options lists it in priceSources.options.A MARKET market lends to professional borrowers only, so it needs a borrower register. See Borrower register on a MARKET market.boolean
default:"false"
FIXED only. true deploys an initialPrice that differs from the instrument’s nominal times its redemption percent, which is otherwise refused with FIXED_PRICE_NOMINAL_MISMATCH. See Fixed price.string
The external feed an
ORACLE market reads prices from. Required when priceSource is ORACLE, and refused for every other price source. The feed is probed before any calldata is handed out. It must answer decimals(), plus latestRoundData() or the latestAnswer() and latestTimestamp() pair. Its price must be positive and dated within the last 24 hours.string
Settlement token (stablecoin) for the market. Any ERC-20 with 6 to 18 decimals, validated on-chain. Omit to use the network’s USDC. Cannot be changed after deployment. See List Borrow Assets.
string
The borrower register, installed on the market by this transaction. Borrowers are examined against it on every loan. It must answer
isVerified(address), as an ERC-3643 identity registry, an ONCHAINID-based register and the Trusset register all do.Optional for NAV and ORACLE. For MARKET, see Borrower register on a MARKET market. Omitted, the collateral token’s own compliance alone governs who may borrow. A register installed here stands at adoption: the lender of record cannot bring a different one. The market admin can change it afterwards with Set Identity Gates.The previous Trusset ID Register, 0xE9114c40934f6fB6BB317935156b731f7A86D006, takes no new identities and is refused with IDENTITY_REGISTRY_RETIRED. Name the current one, 0xEf244c4Bd4d71Bd9A30EDF6Edef7CCC72642291a. See The Previous Trusset ID Register.string
A verification profile key of this instance, 1 to 60 letters, digits,
- or _. The deployment installs that profile’s eligibility register as the borrower register, exactly as if its address were sent in identityRegistry. Sending both is accepted only when they name the same register, otherwise GATE_PROFILE_CONFLICT. A profile without an eligibility register is refused with ELIGIBILITY_REGISTER_NOT_SET.string
The register the deployer proposes for liquidity providers. It is recorded on the factory and installed by nothing: which provider register the market uses is the lender of record’s decision at adoption. It must answer
isVerified(address), exactly like identityRegistry, and the previous Trusset ID Register is refused here too.string
A verification profile key whose eligibility register is proposed as
providerRegistry. The same rules as identityRegistryProfile apply.string
The ID of a whitelist of this instance, 1 to 64 letters, digits,
- or _, installed as the borrower register instead of an address. Not accepted beside identityRegistryProfile, and refused on a MARKET market. See Whitelists as gates.string
The ID of a whitelist of this instance, proposed as
providerRegistry. Not accepted beside providerRegistryProfile.boolean
true confirms that the whitelist proposed as providerRegistry lists identified wallets only. Required whenever providerRegistry is a whitelist, whether named by ID or by address.integer
default:"0"
Decimals to assume if the collateral token’s
decimals() cannot be read on-chain. Integer between 0 and 36.string
Hash of the transaction you broadcast for this operation. Send it to confirm the transaction and record the result. Omit it to receive the calldata.
Borrower register on a MARKET market
AMARKET market carries the free-hand realization path, which is open against professional borrowers only. Its borrower register must therefore qualify as a professional gate: every verification profile that admits wallets to it, on any instance of the market’s network and environment, is limited to professional clients and in the REGISTER mode, and there is at least one. The register deployed for the lender of record’s own PROFESSIONAL profile qualifies, as long as no other profile admits wallets to it. Until the register qualifies, the API opens no loan and adds no liquidity on the market. See Professional borrowers.
A MARKET deployment that nominates candidates and leaves openToAnyCurator off may omit identityRegistry. The nominee then brings its professional register with its adoption terms, and Accept Lender Role checks it. Any other MARKET deployment must install a register, or it is refused with IDENTITY_REGISTRY_REQUIRED.
A register installed here stands at adoption, so the deployment checks it first: a MARKET deployment whose identityRegistry does not qualify is refused with 409 PROFESSIONAL_BORROWER_GATE_REQUIRED, and nothing is built. The collateral token’s own register never qualifies, because it proves identity, not professional status. When the profiles behind the register cannot be read, the deployment answers 503 STATE_UNAVAILABLE instead of a verdict. Nominating the lender of record and letting it bring the register avoids the question.
Fixed price
AFIXED market prices its collateral at initialPrice, per whole collateral token in the settlement token, for its whole life. The price never goes stale, the oracle refuses every signed price, and the price authority can only mark it down through Sync Oracle Price, within the deviation bound. No price update can raise it again, so a price set too low stays. Whoever owns the oracle could still replace its implementation through an upgrade, so the lender of record keeps the oracle owner in its own control. A fixed price is neither a stock-exchange nor a market price, so the free-hand exchange sale is not available as the market’s realization path, and neither is the atomic sale.
The deployment checks the price before building anything:
- The factory must create oracles that can hold a fixed price, or the request is refused with
409 FIXED_PRICE_UNSUPPORTED.503 FACTORY_READ_FAILEDwhen that cannot be read. - The token’s instrument record must name its currency, and the settlement token’s currency must be known to Trusset, or the request is refused with
422 FIXED_PRICE_CURRENCY_UNKNOWN.details.missingisTOKENorSETTLEMENT. A fixed price converts nothing, so both currencies must be the same, or the request is refused with422 FIXED_PRICE_CURRENCY_MISMATCH. initialPriceis compared with the instrument’s nominal per unit times its redemption percent over 100, assuming one whole token is one unit of the instrument. A price that differs is refused with409 FIXED_PRICE_NOMINAL_MISMATCH, anddetailscarriesinitialPrice,expectedPrice,nominal,redemptionPctandcurrency. SendacknowledgeFixedPriceMismatch: trueto deploy it anyway. A record without a nominal is not compared, andwarningssays so.
priceSource=FIXED.
Whitelists as gates
identityRegistryAccessList and providerRegistryAccessList name one of this instance’s whitelists by ID instead of a register address. The rules are those of Set Identity Gates:
- A whitelist cannot be the borrower register of a
MARKETmarket:409 ACCESS_LIST_NOT_ALLOWED. - A whitelist kept for depositors cannot be the borrower register, and one kept for borrowers cannot be the provider register:
409 ACCESS_LIST_PURPOSE_MISMATCH. - A whitelist proposed as
providerRegistrymust be kept by this instance, or owned or managed by one of its verified wallets: otherwise403 ACCESS_LIST_NOT_CONTROLLED. It also needsattestIdentifiedMembers: true: otherwise400 ACCESS_LIST_ATTESTATION_REQUIRED. - A whitelist ID that names no active whitelist of this instance answers
404 ACCESS_LIST_NOT_FOUND, and a profile key beside it400 GATE_SOURCE_CONFLICT.
Rate offering
The market’s interest model is deployed with the market, and the rate offering sets how it prices from the first loan. All values are basis points.string
default:"CURVE"
CURVE, CLAMPED or FIXED.CURVE is the default utilization curve and takes no bounds. CLAMPED is that curve held between a floor and a cap. FIXED stamps one rate onto each loan when it opens, and that rate does not move for the life of the loan.integer
Lower bound for
CLAMPED. Integer, 0 to 50000. Defaults to 0 under CLAMPED.integer
Upper bound for
CLAMPED, and required by it. Integer, 1 to 50000, and at least rateFloorBps. A binding cap never suspends borrowing: the rate saturates at the cap.integer
The stamped rate for
FIXED, and required by it. Integer, 1 to 50000.rateFloorBps or rateCapBps with FIXED, or fixedRateBps with CLAMPED, is refused rather than silently ignored. Sending any bound without a rateMode of CLAMPED or FIXED is refused too. A FIXED market also needs maxLoanDurationDays, because the term clock replaces the rate curve as the pull toward repayment.
The factory also bounds creator-set rates with its creatorRateCeiling, read at build time. Get Deployment Options returns it as creatorRateCeilingBps.
Term offering
A market with no term offering makes open-ended loans. Setting a duration, a terminal date, or both turns terms on, and Open Loan then returns the due date in itsoffering block before the borrower signs.
integer
How long a loan may run before it falls due. Integer number of days, 1 to 3650.
integer | string
A hard date every loan on this market falls due by, whatever its own duration. A unix timestamp in seconds, or an ISO date. Must be in the future.
integer
Days after the due date before the term penalty applies. Integer, 1 to 30. Omitted on a market with terms, the contract applies its 3-day default. Sending it without
maxLoanDurationDays or terminalDueDate is refused.Fee shares
Every market pays its operator a fixed 8 percent and its distributors a fixed 5 percent of repaid interest, both taken after the 15 percent Trusset infrastructure fee. The factory holds both as constants and no party can change them. A deployment therefore carries no share offer, and a request that sendsoperatorShareBps is refused with OPERATOR_SHARE_FIXED.
The operator share is paid to the wallet that takes the lender-of-record role. The calldata response echoes both shares under feeShares.
Asset data
The deployment builds the market’s asset data bundle from the token as this instance imported it. The bundle holds the chain, factory, collateral mode, price source and settlement asset, the token’s recorded facts, the issuer’s published profile, and the token’s instrument record. See What the bundle contains. The bundle’scontentHash rides inside deployMarket, so the signature that creates the market also attests the data. A token without a published profile is refused with TOKEN_PROFILE_REQUIRED.
The calldata response returns the exact bytes being attested under assetData, so the deploying wallet sees what it signs for. Preview Asset Data returns the same bundle before anything is built. A lender of record reviews it and names the hash it read when it takes the role.
Response Fields
WithouttxHash, the response is the unsigned factory transaction.
object
Show child attributes
Show child attributes
string
SIGN_TRANSACTION.object
Unsigned transaction,
{ to, data, value, chainId }, addressed to the lending factory.string
deployMarket.string
Collateral token, lowercased.
string
FREEZE or CUSTODY.string
NAV, MARKET, ORACLE or FIXED.object
FIXED only. { priceWei, price, settlementSymbol, settlementCurrency, tokenCurrency, perWholeToken, neverStale, markDownOnly, oracleOwnerCanUpgrade, check }. check carries nominal, redemptionPct, expectedPrice, matchesNominal and acknowledgedMismatch.array
FIXED only. Plain-language statements of what a fixed price means, and of an acknowledged or unchecked nominal. Show them before the deployment is signed.boolean
true when the request carried attestIdentifiedMembers: true. Absent otherwise.object
The probed feed,
{ feedAddress, decimals, answer, updatedAt, shape }. shape is latestRoundData or legacy. Present only for an ORACLE market.object
{ address, symbol, decimals } for the settlement asset the market will use.string
The instance’s registered wallet, which is the address this transaction expects to be signed by.
object
The asset data this deployment attests.
Show child attributes
Show child attributes
string
keccak256 of
canonicalJson. This is the hash the deployment writes to the factory.string
Bundle schema version,
1.1.0.string
The exact bytes hashed: the bundle as RFC 8785 canonical JSON.
object
The same bundle as an object, with
schemaVersion, market, token, profile and instrument. Empty members are omitted.string
The wallet the attestation will name, the same as
signer.string
What the signature attests.
array
Nominated candidates, lowercased. Empty when none were named.
boolean
Always
true on a new deployment.boolean
Whether the market will also be offered to every approved lender of record.
object
object
Show child attributes
Show child attributes
integer
The duration in seconds, as the contract will hold it.
0 when no duration was set.integer
The grace period in seconds.
0 when none was set.integer
Unix seconds, or
null when no terminal date was set.boolean
true when terms are on and no grace period was named, so the contract’s 3-day default applies.integer
The fixed operator share read from the factory, in basis points of interest left after the infrastructure fee.
800.object
{ operatorBps, distributorBps, fixed, ofBase }. fixed is always true and ofBase is AFTER_INFRASTRUCTURE.boolean
Always
false on this branch. A market that would collide is refused with MARKET_EXISTS instead.object
Present when this instance already records a market for the same token, mode and settlement asset that the configured factory holds no deployment for. Such a market cannot be adopted or operated here, so it does not block this deployment. Carries
marketId, marketAddress, reason (MARKET_NOT_IN_FACTORY), note and archiveWith. archiveWith names the archive route of the Trusset issuer app, which has no API key counterpart.object
How to confirm:
{ endpoint, field, required, body, description }. body holds the cross-check fields, with txHash left null for you to fill in.txHash. Every other field is an optional cross-check against the deployment the receipt proves: a supplied collateralTokenAddress, mode or borrowAssetAddress that differs returns TX_WRONG_ARGS. Drop any confirmWith.body field that is null before sending it, because this endpoint’s schema refuses null for an address.
Confirming returns 201 with the recorded market and its readiness state.
object
Show child attributes
Show child attributes
object
The persisted market record:
id, all contract addresses, collateralMode, priceSource, collateralDecimals, the borrow asset triple and operatorShare. It also carries the rate and term columns read from the chain, and seeded default risk parameters.string
Deployment transaction hash.
string
The instance’s registered wallet address, or null when no wallet is registered.
string
Router deployed for this market.
object
Contract wiring state, with
fullyConfigured.object
Whether the adapter is authorized on the collateral token, and what is required. See Get Configuration Status.
boolean
Whether the market’s liquidation router may receive the collateral token.
boolean
Whether the registered wallet can sign prices for this market’s oracle.
boolean
Whether the registered wallet holds
LIQUIDATOR_ROLE on the market.boolean
true while the market still has no lender of record.array
Outstanding steps, each with
step, description, requiredRole, requiredBy.object
Ready-to-sign calldata keyed by step name, each
{ to, data, description }.object
The attestation read from the deployment receipt:
{ contentHash, previousHash, version, attestor, bundleAvailable }. version is 1 for a new market. null when the receipt carries no attestation.A recorded market blocks a redeployment only while the configured factory still holds its deployment. A record the factory does not know belongs to a superseded factory generation, so the calldata response reports it under
supersedes instead of refusing.curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/deploy" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"initialPrice": "104.82",
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"mode": "FREEZE",
"priceSource": "NAV",
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200,
"maxLoanDurationDays": 180
}'
const base = 'https://api.trusset.org/lending-external-securities-v2/api/markets/deploy';
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const build = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
collateralTokenAddress: '0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f',
initialPrice: '104.82',
candidates: [candidateAddress],
mode: 'CUSTODY',
borrowAssetAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F'
})
});
const { data: calldata } = await build.json();
const tx = await wallet.sendTransaction(calldata.transaction);
await tx.wait();
const confirm = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
txHash: tx.hash,
collateralTokenAddress: calldata.collateralTokenAddress
})
});
const { data: recorded } = await confirm.json();
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"data": "0x...",
"value": "0",
"chainId": 11155111
},
"functionName": "deployMarket",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAsset": {
"address": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"symbol": "USDC",
"decimals": 6
},
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"schemaVersion": "1.1.0",
"canonicalJson": "{\"market\":{\"borrowAsset\":\"0x98ad0ca091552e23c564b41c74282e5343d03e8a\",\"chainId\":11155111,\"collateralMode\":\"FREEZE\",\"factory\":\"0xf1ad12f5698f58badd8a662acffccb067a2ddb8a\",\"family\":\"FUNGIBLE\",\"priceSource\":\"NAV\"},\"profile\":{\"isin\":\"DE000A2YN900\",\"issuerName\":\"ACME Industrial AG\"},\"schemaVersion\":\"1.1.0\",\"token\":{\"decimals\":18,\"name\":\"ACME Industrial Bond 2031\",\"symbol\":\"ACME\",\"tokenAddress\":\"0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f\"}}",
"bundle": {
"schemaVersion": "1.1.0",
"market": {
"chainId": 11155111,
"factory": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"family": "FUNGIBLE",
"borrowAsset": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"collateralMode": "FREEZE",
"priceSource": "NAV"
},
"token": {
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"name": "ACME Industrial Bond 2031",
"symbol": "ACME",
"decimals": 18
},
"profile": {
"issuerName": "ACME Industrial AG",
"isin": "DE000A2YN900"
}
},
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"note": "The deployment signature attests this bundle: assetData(market).attested equals contentHash and attestor equals the signing wallet once the transaction mines."
},
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"seekingCurator": true,
"openToAnyCurator": false,
"rateOffering": {
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200
},
"termOffering": {
"maxLoanDurationSeconds": 15552000,
"termGracePeriodSeconds": 0,
"terminalDueDate": null,
"graceDefaultApplies": true
},
"operatorShareBps": 800,
"feeShares": {
"operatorBps": 800,
"distributorBps": 500,
"fixed": true,
"ofBase": "AFTER_INFRASTRUCTURE"
},
"alreadyExists": false,
"confirmWith": {
"endpoint": "POST /lending-external-securities-v2/api/markets/deploy",
"field": "txHash",
"required": ["txHash"],
"body": {
"txHash": null,
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"identityRegistry": null
},
"description": "Send the mined deployMarket transaction hash as txHash. Every other field is optional: the record is read from the deployment the receipt proves, and a supplied field is only cross-checked against it."
}
}
}
{
"success": true,
"data": {
"market": {
"id": "clx_secmarket_001",
"marketAddress": "0x70a0e25c7b768b87e658348b3b577678a173e038",
"oracleAddress": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"insuranceFundAddress": "0x8f1a6b30c7d24e95f0a3b81d6c47e2905fa3b9c2",
"adapterAddress": "0x4d2b93f70e18c6a5d34b027fe95c81a6730d77ae",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"collateralMode": "FREEZE",
"collateralDecimals": 18,
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"borrowAssetSymbol": "USDC",
"borrowAssetDecimals": 6,
"priceSource": "NAV",
"seekingCurator": true,
"curator": null,
"realizationMode": "UNDECLARED",
"operatorShare": "800",
"collateralFactor": "7500",
"liquidationThreshold": "8500",
"active": true
},
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b",
"relayerAddress": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"configurationStatus": { "fullyConfigured": false },
"collateralAuthStatus": {
"mode": "FREEZE",
"adapterAuthorized": false,
"interfaceSupported": true,
"requirement": "adapter_must_hold_agent_role"
},
"routerHolderAuthorized": false,
"oracleSignerAuthorized": false,
"liquidatorRoleGranted": false,
"pendingCurator": true,
"configurationSteps": [
{
"step": "grantAdapterAgentRole",
"description": "Grant agent role to freeze adapter on the security token",
"requiredRole": "TOKEN_AGENT_ADMIN",
"requiredBy": ""
}
],
"configurationCalldata": {
"grantAdapterAgentRole": {
"to": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"data": "0x...",
"description": "Grant agent role to freeze adapter on the security token",
"value": "0",
"chainId": 11155111
}
},
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"version": 1,
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"bundleAvailable": true
}
}
}
{
"success": false,
"error": {
"code": "OPERATOR_SHARE_FIXED",
"message": "operatorShareBps is no longer accepted: every market pays its operator a fixed 8 percent and its distributors a fixed 5 percent of the interest left after the 15 percent infrastructure fee, set by the contracts and read from the factory (OPERATOR_SHARE, DISTRIBUTOR_SHARE). Remove the field from the request."
}
}
{
"success": false,
"error": {
"code": "TOKEN_PROFILE_REQUIRED",
"message": "ACME has no published profile, so there is no asset data to attest and no market can be created against it. Open the token in Assets and fill in at least one field of its published profile, the overview this instance can always edit: the description, the issuer name, the sector, the website, the AMM contract or the reserve document (PUT /external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile from the issuer app, or PUT /external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile with an API key), then create the market: the issuer's own wallet attests that data inside the deployment signature. The instrument record with the identifiers, the jurisdiction and the offering documents is published by the token's controller only: by this instance when one of its verified wallets controls the token (PUT /instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f from the issuer app, or PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with an API key), otherwise through the API with the controller's signature (POST /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request, then PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with issuerApproval). A record already published for the token is part of the asset data either way.",
"details": {
"field": "profile",
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"imported": true,
"nextStep": "FILL_PROFILE",
"overviewFields": ["description", "issuerName", "sector", "website", "ammAddress", "reserveURI"],
"editProfile": "/external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"apiPaths": {
"editProfile": "/external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"instrumentApprovalRequest": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request"
}
}
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "rateCapBps",
"message": "A FIXED rate mode takes fixedRateBps only; rateFloorBps and rateCapBps belong to CLAMPED. Drop them or choose CLAMPED."
}
]
}
}
Error Codes
| Code | HTTP | Cause |
|---|---|---|
VALIDATION_ERROR | 400 | A field failed schema validation, including a rate or term offering that mixes modes, a missing initialPrice for NAV, MARKET or FIXED, oracleAddress sent or missing against the price source, a profile key that is not 1 to 60 letters, digits, - or _, and a whitelist ID that is not 1 to 64 of them. error.details names each offending field |
GATE_SOURCE_CONFLICT | 400 | One side names both a verification profile and a whitelist, or an address that differs from the whitelist named beside it |
ACCESS_LIST_ATTESTATION_REQUIRED | 400 | providerRegistry is a whitelist and attestIdentifiedMembers is not true |
OPERATOR_SHARE_FIXED | 400 | operatorShareBps was sent. The shares are fixed by the contracts |
BORROW_ASSET_IS_COLLATERAL | 400 | borrowAssetAddress and collateralTokenAddress are the same token |
COLLATERAL_TOKEN_NOT_CONTRACT | 400 | No deployed bytecode at collateralTokenAddress on this network |
COLLATERAL_TOKEN_NOT_ERC20 | 400 | The token does not answer balanceOf and decimals, so the factory would refuse it |
FREEZE_MODE_UNSUPPORTED_TOKEN | 400 | mode is FREEZE but the token exposes no ERC-3643 freeze surface (isAgent and getFrozenTokens), or it is an ERC-7943 token. Deploy in CUSTODY mode instead |
CUSTODY_MODE_EXCLUDED | 400 | The factory excludes custody mode for this token. Deploy a FREEZE market instead |
INVALID_BORROW_ASSET | 400 | borrowAssetAddress is not a valid address |
BORROW_ASSET_NOT_CONTRACT | 400 | No deployed bytecode at borrowAssetAddress |
BORROW_ASSET_NOT_ERC20 | 400 | decimals() or balanceOf() could not be read on the borrow asset |
BORROW_ASSET_DECIMALS_UNSUPPORTED | 400 | Borrow asset decimals outside 6 to 18 |
INVALID_IDENTITY_REGISTRY | 400 | identityRegistry or providerRegistry fails its EIP-55 checksum |
GATE_PROFILE_CONFLICT | 400 | identityRegistry or providerRegistry names a different register than the profile sent beside it. details.field names the side |
GATE_PROFILE_UNRESOLVED | 400 | A profile key could not be resolved for a reason that carries no code of its own |
ACCESS_LIST_NOT_CONTROLLED | 403 | providerRegistry is a whitelist another instance curates, and no verified wallet of this instance owns or manages it |
IMPORT_NOT_FOUND | 404 | collateralTokenAddress is not a security token this instance imported or issued |
PROFILE_NOT_FOUND | 404 | identityRegistryProfile or providerRegistryProfile names no verification profile of this instance |
ACCESS_LIST_NOT_FOUND | 404 | identityRegistryAccessList or providerRegistryAccessList names no whitelist of this instance on this network that is not archived |
ELIGIBILITY_REGISTER_NOT_SET | 409 | The named profile has no eligibility register |
ACCESS_LIST_NOT_ALLOWED | 409 | The borrower register of a MARKET deployment is a whitelist |
PROFESSIONAL_BORROWER_GATE_REQUIRED | 409 | The identityRegistry of a MARKET deployment is not the eligibility register of verification profiles limited to professional clients. See Borrower register on a MARKET market |
ACCESS_LIST_PURPOSE_MISMATCH | 409 | A whitelist is kept for the other side: DEPOSITOR as the borrower register or BORROWER as providerRegistry |
COLLATERAL_GRANULARITY_UNSUPPORTED | 409 | The collateral token’s ERC-1400 granularity() is above one base unit, so it would refuse the exact amounts a liquidation seizes |
FIXED_PRICE_UNSUPPORTED | 409 | priceSource is FIXED and the factory still creates oracles that cannot hold a fixed price. Deploy with NAV |
FIXED_PRICE_NOMINAL_MISMATCH | 409 | initialPrice differs from the instrument’s nominal times its redemption percent. Check the price, then send acknowledgeFixedPriceMismatch: true to deploy it |
INVALID_ORACLE_ADDRESS | 400 | oracleAddress is the zero address or fails its checksum |
INVALID_INITIAL_PRICE | 400 | initialPrice carries more decimal places than the borrow asset supports, or rounds to zero at its scale |
INVALID_RATE_OFFERING | 400 | A CLAMPED cap below its floor |
RATE_ABOVE_CREATOR_CEILING | 400 | The rate offering exceeds the factory’s creator rate ceiling. The message names the ceiling |
INVALID_TERM_OFFERING | 400 | terminalDueDate is not a unix timestamp or ISO date, or is not in the future |
DEPLOY_FAILED | 400 | The deployment could not be prepared for a reason that carries no code of its own. message says why |
MARKET_EXISTS | 409 | This instance already runs a market for this token, mode and settlement asset that the factory holds. error carries action: "USE_EXISTING_MARKET", marketId and archiveWith |
MARKET_UNRECORDED | 409 | A matching market was already deployed by this instance’s wallet and never recorded. details carries marketAddress, deployer and txHash. Record it by sending that txHash with collateralTokenAddress to this endpoint instead of deploying again |
WALLET_NOT_CONFIGURED | 412 | This instance has no verified wallet to sign the deployment |
TOKEN_PROFILE_REQUIRED | 422 | The token has no published profile, so there is no asset data to attest. details.editProfile names where to add it |
IDENTITY_REGISTRY_REQUIRED | 422 | priceSource is MARKET, no borrower register was named, and the deployment does not nominate its lender of record (no candidates, or openToAnyCurator on). details carries field and priceSource |
IDENTITY_REGISTRY_INCOMPATIBLE | 422 | identityRegistry or providerRegistry does not answer isVerified(address). details names the field and the register |
IDENTITY_REGISTRY_RETIRED | 422 | identityRegistry or providerRegistry is the previous Trusset ID Register, which takes no new identities. details carries field, registry and currentRegistry. Name the current register |
ORACLE_FEED_NOT_A_CONTRACT | 422 | No contract at oracleAddress |
ORACLE_FEED_UNUSABLE | 422 | The feed does not answer decimals(), answers neither price shape, or answers a non-positive or undated price |
ORACLE_FEED_STALE | 422 | The feed’s price is older than 24 hours |
FIXED_PRICE_CURRENCY_UNKNOWN | 422 | FIXED only. The token’s instrument record names no currency, or the settlement token’s currency is not known. details.missing is TOKEN or SETTLEMENT |
FIXED_PRICE_CURRENCY_MISMATCH | 422 | FIXED only. The token’s currency and the settlement token’s currency differ |
PROVIDER_UNAVAILABLE | 503 | The collateral token could not be read on-chain, so no transaction was prepared |
IDENTITY_REGISTRY_UNREADABLE | 503 | A named register could not be reached to check it. Retry shortly |
STATE_UNAVAILABLE | 503 | MARKET only. The verification profiles behind identityRegistry could not be read, so whether it qualifies as a professional gate is unknown and nothing was built. Retry shortly |
ORACLE_FEED_UNVERIFIABLE | 503 | The feed could not be checked right now. Retry shortly |
INSTRUMENT_UNREADABLE | 503 | The token’s instrument record could not be read, so the asset data bundle could not be built. Retry shortly |
ACCESS_LIST_UNREADABLE | 503 | Whether a named address is a whitelist, or whether a wallet of this instance owns or manages it, could not be read. Retry shortly |
ACCESS_LISTS_NOT_CONFIGURED | 503 | A whitelist ID was sent on a network where the whitelist factory is not deployed |
FACTORY_READ_FAILED | 503 | The factory could not be read to validate the offering, to confirm the fixed shares, to tell whether it creates fixed-price oracles, or to tell whether an existing market is still operable. Nothing was built. Retry shortly |
FACTORY_UPGRADE_REQUIRED | 503 | The factory runs an implementation without a liquidation router, without asset data attestation, or without fixed fee shares. Nothing was built |
txHash can also return:
| Code | HTTP | Cause |
|---|---|---|
TX_WRONG_ARGS | 400 | The transaction deployed a market for a different collateral token, collateral mode or settlement asset than the one supplied |
TX_NOT_VERIFIED | 400 | The transaction deployed no market |
DEPLOYMENT_NOT_INSTANCE_BOUND | 403 | Neither the signer nor any party of the deployment is a verified wallet of this instance |
MARKET_EXISTS | 409 | This market is already recorded in this instance |
FACTORY_MISMATCH | 409 | The transaction did not target the lending factory this instance deploys to |
ADAPTER_PLEDGEE_UNSUPPORTED | 422 | The collateral adapter does not record a pledgee per lock, so encumbrances would name no attributable beneficiary |
DEPLOYMENT_UNREADABLE | 503 | The deployed market could not be read from the factory |
FACTORY_READ_FAILED | 503 | The factory could not be reached to resolve the market. Retry shortly |
BORROW_ASSET_UNREADABLE | 503 | The market’s settlement asset could not be read and none was supplied |
borrowAssetAddress is validated on the confirm call too, so the four borrow asset codes above can also appear there. Confirming can also return any transaction verification error.⌘I
curl -X POST "https://api.trusset.org/lending-external-securities-v2/api/markets/deploy" \
-H "X-API-Key: trusset_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"initialPrice": "104.82",
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"mode": "FREEZE",
"priceSource": "NAV",
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200,
"maxLoanDurationDays": 180
}'
const base = 'https://api.trusset.org/lending-external-securities-v2/api/markets/deploy';
const headers = {
'X-API-Key': 'trusset_your_key_here',
'Content-Type': 'application/json'
};
const build = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
collateralTokenAddress: '0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f',
initialPrice: '104.82',
candidates: [candidateAddress],
mode: 'CUSTODY',
borrowAssetAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F'
})
});
const { data: calldata } = await build.json();
const tx = await wallet.sendTransaction(calldata.transaction);
await tx.wait();
const confirm = await fetch(base, {
method: 'POST',
headers,
body: JSON.stringify({
txHash: tx.hash,
collateralTokenAddress: calldata.collateralTokenAddress
})
});
const { data: recorded } = await confirm.json();
{
"success": true,
"data": {
"action": "SIGN_TRANSACTION",
"transaction": {
"to": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"data": "0x...",
"value": "0",
"chainId": 11155111
},
"functionName": "deployMarket",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAsset": {
"address": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"symbol": "USDC",
"decimals": 6
},
"signer": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"schemaVersion": "1.1.0",
"canonicalJson": "{\"market\":{\"borrowAsset\":\"0x98ad0ca091552e23c564b41c74282e5343d03e8a\",\"chainId\":11155111,\"collateralMode\":\"FREEZE\",\"factory\":\"0xf1ad12f5698f58badd8a662acffccb067a2ddb8a\",\"family\":\"FUNGIBLE\",\"priceSource\":\"NAV\"},\"profile\":{\"isin\":\"DE000A2YN900\",\"issuerName\":\"ACME Industrial AG\"},\"schemaVersion\":\"1.1.0\",\"token\":{\"decimals\":18,\"name\":\"ACME Industrial Bond 2031\",\"symbol\":\"ACME\",\"tokenAddress\":\"0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f\"}}",
"bundle": {
"schemaVersion": "1.1.0",
"market": {
"chainId": 11155111,
"factory": "0xf1ad12f5698f58badd8a662acffccb067a2ddb8a",
"family": "FUNGIBLE",
"borrowAsset": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"collateralMode": "FREEZE",
"priceSource": "NAV"
},
"token": {
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"name": "ACME Industrial Bond 2031",
"symbol": "ACME",
"decimals": 18
},
"profile": {
"issuerName": "ACME Industrial AG",
"isin": "DE000A2YN900"
}
},
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"note": "The deployment signature attests this bundle: assetData(market).attested equals contentHash and attestor equals the signing wallet once the transaction mines."
},
"candidates": ["0x4e91a7c05d3b62f18a0c94e7db2358f1c60a4e93"],
"seekingCurator": true,
"openToAnyCurator": false,
"rateOffering": {
"rateMode": "CLAMPED",
"rateFloorBps": 200,
"rateCapBps": 1200
},
"termOffering": {
"maxLoanDurationSeconds": 15552000,
"termGracePeriodSeconds": 0,
"terminalDueDate": null,
"graceDefaultApplies": true
},
"operatorShareBps": 800,
"feeShares": {
"operatorBps": 800,
"distributorBps": 500,
"fixed": true,
"ofBase": "AFTER_INFRASTRUCTURE"
},
"alreadyExists": false,
"confirmWith": {
"endpoint": "POST /lending-external-securities-v2/api/markets/deploy",
"field": "txHash",
"required": ["txHash"],
"body": {
"txHash": null,
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"mode": "FREEZE",
"priceSource": "NAV",
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"identityRegistry": null
},
"description": "Send the mined deployMarket transaction hash as txHash. Every other field is optional: the record is read from the deployment the receipt proves, and a supplied field is only cross-checked against it."
}
}
}
{
"success": true,
"data": {
"market": {
"id": "clx_secmarket_001",
"marketAddress": "0x70a0e25c7b768b87e658348b3b577678a173e038",
"oracleAddress": "0x3b25752c1459c5cf1b0bfcfdf0d56883c8047423",
"insuranceFundAddress": "0x8f1a6b30c7d24e95f0a3b81d6c47e2905fa3b9c2",
"adapterAddress": "0x4d2b93f70e18c6a5d34b027fe95c81a6730d77ae",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"collateralTokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"collateralMode": "FREEZE",
"collateralDecimals": 18,
"borrowAssetAddress": "0x98ad0ca091552e23c564b41c74282e5343d03e8a",
"borrowAssetSymbol": "USDC",
"borrowAssetDecimals": 6,
"priceSource": "NAV",
"seekingCurator": true,
"curator": null,
"realizationMode": "UNDECLARED",
"operatorShare": "800",
"collateralFactor": "7500",
"liquidationThreshold": "8500",
"active": true
},
"txHash": "0x9f2c41d8b7e05a3164c2870fbd935e1a4c7802db6135ea9048f7c21b5d3ea41b",
"relayerAddress": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"liquidationRouterAddress": "0x067a3feea46649adad8e31c33b3a4d8774b8d8cf",
"configurationStatus": { "fullyConfigured": false },
"collateralAuthStatus": {
"mode": "FREEZE",
"adapterAuthorized": false,
"interfaceSupported": true,
"requirement": "adapter_must_hold_agent_role"
},
"routerHolderAuthorized": false,
"oracleSignerAuthorized": false,
"liquidatorRoleGranted": false,
"pendingCurator": true,
"configurationSteps": [
{
"step": "grantAdapterAgentRole",
"description": "Grant agent role to freeze adapter on the security token",
"requiredRole": "TOKEN_AGENT_ADMIN",
"requiredBy": ""
}
],
"configurationCalldata": {
"grantAdapterAgentRole": {
"to": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"data": "0x...",
"description": "Grant agent role to freeze adapter on the security token",
"value": "0",
"chainId": 11155111
}
},
"assetData": {
"contentHash": "0x5ec0cb1ff9fff28a94e834046e4b31f3e539afb47eafe70fc9a06983b04147c3",
"previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"version": 1,
"attestor": "0x1234f9a07c6b53d81e2a4f70c9b385d6014a7e52",
"bundleAvailable": true
}
}
}
{
"success": false,
"error": {
"code": "OPERATOR_SHARE_FIXED",
"message": "operatorShareBps is no longer accepted: every market pays its operator a fixed 8 percent and its distributors a fixed 5 percent of the interest left after the 15 percent infrastructure fee, set by the contracts and read from the factory (OPERATOR_SHARE, DISTRIBUTOR_SHARE). Remove the field from the request."
}
}
{
"success": false,
"error": {
"code": "TOKEN_PROFILE_REQUIRED",
"message": "ACME has no published profile, so there is no asset data to attest and no market can be created against it. Open the token in Assets and fill in at least one field of its published profile, the overview this instance can always edit: the description, the issuer name, the sector, the website, the AMM contract or the reserve document (PUT /external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile from the issuer app, or PUT /external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile with an API key), then create the market: the issuer's own wallet attests that data inside the deployment signature. The instrument record with the identifiers, the jurisdiction and the offering documents is published by the token's controller only: by this instance when one of its verified wallets controls the token (PUT /instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f from the issuer app, or PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with an API key), otherwise through the API with the controller's signature (POST /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request, then PUT /instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f with issuerApproval). A record already published for the token is part of the asset data either way.",
"details": {
"field": "profile",
"tokenAddress": "0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"imported": true,
"nextStep": "FILL_PROFILE",
"overviewFields": ["description", "issuerName", "sector", "website", "ammAddress", "reserveURI"],
"editProfile": "/external-tokens/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"apiPaths": {
"editProfile": "/external-tokens/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/profile",
"instrumentRecord": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f",
"instrumentApprovalRequest": "/instruments/api/0x9f8c1d4b2e7a3056c1b8f4d29e0a7c3518b6d24f/approval-request"
}
}
}
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": [
{
"field": "rateCapBps",
"message": "A FIXED rate mode takes fixedRateBps only; rateFloorBps and rateCapBps belong to CLAMPED. Drop them or choose CLAMPED."
}
]
}
}
