Take out a loan
This guide explains how to take out a loan from an inventory pool
Prerequisites
An account that has been whitelisted as a borrower on the target inventory pool
A sufficient amount of collateral deposited
Process Overview
Request a borrow digest from the API
Sign the digest with your private key
Submit the borrow request with your signature
Execute the resulting transaction on-chain
Step 1: Request Borrow Digest
This step authenticates your request, preventing any other address from requesting a borrow on your behalf.
Make a GET request to https://api.nomial.io/borrow/digest
with the following query parameters:
chain_id
The chain ID (e.g., 1
for Ethereum mainnet)
pool
The inventory pool address
borrower
Your borrower address
amount
The amount of ERC20 to borrow (in base units)
recipient
The address that will receive the borrowed funds (can be same as borrower)
expiry
Unix timestamp when the request expires (e.g., 1 hour in the future)
salt
Arbitrary 32-byte hex string to ensure request uniqueness
Example
curl "https://api.nomial.io/borrow/digest?\
chain_id=1&\
pool=0xabc123...&\
borrower=0xdef456...&\
amount=1000000&\
recipient=0xdef456...&\
expiry=1234567890&\
salt=0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef"
The API will return a digest that needs to be signed by your borrower address
{
"digest": "0x..." // The digest to sign
}
Step 2: Sign the Digest
Sign the provided hex digest directly with the private key of your borrower address. The resulting signature must use the standard 65-byte Ethereum format (r, s, v).
Be sure to sign the raw digest, without including an ERC-191 (“Ethereum Signed Message”) prefix.
Using Foundry cast to sign manually
You can use the cast CLI tool to sign the digest manually with the cast wallet sign command
cast wallet sign --private-key <BORROWER_PRIVATE_KEY> --no-hash <DIGEST>
Step 3: Submit Borrow Request
Make a POST request to https://api.nomial.io/borrow
. This should include identical parameters and values to your digest request, with the addition of signature
(your signature of the request digest).
{
"chain_id": "1", // The chain ID
"pool": "0x...", // The inventory pool address
"borrower": "0x...", // Your borrower address
"amount": "1000000", // The amount to borrow in base units
"recipient": "0x...", // The address that will receive the borrowed funds
"expiry": "1234567890", // Unix timestamp when the request expires
"salt": "0x...", // The same salt used for the request digest
"signature": "0x..." // Your signature of the request digest
}
If your borrow request is valid, the API will return a transaction with validator signatures that can be executed to the inventory pool's owner (access manager):
{
"call": {
"chain": {
"id": "1",
"name": "ethereum"
},
"contract": {
"name": "InventoryPoolDefaultAccessManager01",
"address": "0x..."
},
"function": "borrow(address pool, uint256 amount, address recipient, uint256 expiry, bytes32 salt, bytes[] signatures)",
"selector": "0x...",
"calldata": "0x...",
"params": [
{
"name": "pool",
"value": "0x..."
},
{
"name": "amount",
"value": "1000000"
},
{
"name": "recipient",
"value": "0x..."
},
{
"name": "expiry",
"value": "1234567890"
},
{
"name": "salt",
"value": "0x..."
},
{
"name": "signatures",
"value": ["0x...", "0x..."]
}
]
},
"tx": {
"to": "0x...", // The access manager contract address
"data": "0x...", // The encoded function call data
"value": "0", // The amount of native token to send
"chain_id": "1" // The chain ID
},
"signed_digest": "0x...", // The digest that was signed
"validator_signatures": [ // Array of validator signatures
{
"validator": "validator1",
"signature": "0x..."
}
]
}
Step 4: Execute the Transaction
You can submit your signed transaction to the chain it was intended for. Here's an example using ethers.js:
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const tx = {
to: response.tx.to,
data: response.tx.data,
value: response.tx.value
};
// Send the transaction
const result = await wallet.connect(provider).sendTransaction(tx);
await result.wait();
Alternatively, you can construct the transaction yourself using the call
object:
// Import the contract ABI
const accessManagerABI = [
"function borrow(address pool, uint256 amount, address recipient, uint256 expiry, bytes32 salt, bytes[] signatures)"
];
// Create contract instance
const accessManager = new ethers.Contract(
response.call.contract.address,
accessManagerABI,
wallet
);
// Execute the borrow function
const result = await accessManager.borrow(
response.call.params[0].value, // pool
response.call.params[1].value, // amount
response.call.params[2].value, // recipient
response.call.params[3].value, // expiry
response.call.params[4].value, // salt
response.call.params[5].value // signatures
);
await result.wait();
Note that you must execute your transaction on the access manager contract that is owner
on the inventory pool, and not the inventory pool itself.
Last updated