Creates an offer on an asset, vault, or collection. Requires the caller to sign and provide the parameters of the offer as authorization.
Creating Loan Terms
Here you'll find example code and payload for creating loan terms over our API. Please note that the wallet signing terms must have approved the funds with the principal amount with our LoanCore.
Loan Core V3 Address
0x89bc08BA00f135d608bc335f6B33D7a9ABCC98aF
Example Payload
{
"loanTerms": {
"durationSecs": 8640000,
"principal": "1000000",
"interestRate": "2000000000000000000000",
"collateralAddress": "0x3f228cbcec3ad130c45d21664f2c7f5b23130d23",
"collateralId": "-1",
"payableCurrency": "0x07865c6e87b9f70255377e024ace6630c1eaa37f",
"deadline": "1694375092",
"affiliateCode": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"collectionId": "0x3f228cbcec3ad130c45d21664f2c7f5b23130d23",
"signature": "0x99cd41a8af42772b6a34236b7da2f8a1d30d0cbcf83d1a64ac267ed58e800da214ee595029d927051551511e994bcac91951a755686197626a8e8fa6d50708101b",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "60",
"kind": "collection",
"role": "lender",
// ONLY REQUIRED FOR COLLECTION WIDE OFFERS
"itemPredicates": [
{
"verifier": "0x122a2d5D54A546708a80e7275b96D772f3826ffA",
"data": "0x0000000000000000000000003f228cbcec3ad130c45d21664f2c7f5b23130d23"
}
]
}
Signing LoanTerms
Example Code
const domain = {
name: 'OriginationController',
version: '3',
chainId: '1',
verifyingContract: '0xB7BFcca7D7ff0f371867B770856FAc184B185878'
};
// FOR DIRECT OFFERS ON AN ASSET OR VAULT OFFERS
const typedLoanTermsData = {
LoanTerms: [
{ name: 'proratedInterestRate', type: 'uint256' },
{ name: 'principal', type: 'uint256' },
{ name: 'collateralAddress', type: 'address' },
{ name: 'durationSecs', type: 'uint96' },
{ name: 'collateralId', type: 'uint256' },
{ name: 'payableCurrency', type: 'address' },
{ name: 'deadline', type: 'uint96' },
{ name: 'affiliateCode', type: 'bytes32' },
{ name: 'nonce', type: 'uint160' },
{ name: 'side', type: 'uint8' },
],
};
const exampleTerms = {
durationSecs: 8640000,
principal: '1000000',
interestRate: '2000000000000000000000',
collateralAddress: '0x3f228cbcec3ad130c45d21664f2c7f5b23130d23',
collateralId: '-1',
payableCurrency: '0x07865c6e87b9f70255377e024ace6630c1eaa37f',
deadline: '1694375092',
affiliateCode:
'0x0000000000000000000000000000000000000000000000000000000000000000',
};
async function createLoanTermsSignature(
terms,
signer,
nonce,
role,
) {
const side = role === 'borrower' ? 0 : 1;
const message = { ...terms, nonce, side };
return signer._signTypedData(
domain,
typedLoanTermsData,
message
);
}
// FOR COLLECTION WIDE OFFERS
const typedLoanTermsItemsData = {
LoanTermsWithItems: [
{ name: 'proratedInterestRate', type: 'uint256' },
{ name: 'principal', type: 'uint256' },
{ name: 'collateralAddress', type: 'address' },
{ name: 'durationSecs', type: 'uint96' },
{ name: 'items', type: 'Predicate[]' },
{ name: 'payableCurrency', type: 'address' },
{ name: 'deadline', type: 'uint96' },
{ name: 'affiliateCode', type: 'bytes32' },
{ name: 'nonce', type: 'uint160' },
{ name: 'side', type: 'uint8' },
],
Predicate: [
{ name: 'data', type: 'bytes' },
{ name: 'verifier', type: 'address' },
],
};
const verifier = '0x1B6e58AaE43bFd2a435AA348F3328f3137DDA544';
const collection = '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d' // BAYC
// SCROLL TO THE BOTTOM OF THE PAGE TO SEE EXAMPLE CODE ON HOW TO GENERATE THIS
const exampleItems = [
{
data: '0x0000000000000000000000003f228cbcec3ad130c45d21664f2c7f5b23130d23',
verifier: '0x122a2d5D54A546708a80e7275b96D772f3826ffA',
}
]
async function createLoanItemsSignature(
terms,
items,
signer,
nonce,
role,
) {
const side = role === 'borrower' ? 0 : 1;
const message = { ...terms, items, nonce, side };
return signer._signTypedData(
domain,
typedLoanTermsItemsData,
message
);
}
V2 → V3 Migration
Body / Payload Changes
Removed Properties
These properties are no longer being used. They can be sent, but will be dropped by the API.
numInstallments
itemsHash
Added Properties
There is a new required property, affiliateCode
that will enable a referral program in the future. This is currently not in use so send a 32 byte 0 hash. You can use the ethers v5 constant ethers.constants.HashZero or v6 ethers.ZeroHash.
affiliateCode
- "0x0000000000000000000000000000000000000000000000000000000000000000"
Moved Properties
The following property schema is the same but has changed positions in the payload.
itemPredicates
- Moved up from
body.loanTerms
to the root ofbody
.
- Moved up from
Signing Changes
New OriginationController Address
0xB7BFcca7D7ff0f371867B770856FAc184B185878
Updated Domain
{
"name": "OriginationController",
"version": "3",
"chainId": "1",
"verifyingContract": "0xB7BFcca7D7ff0f371867B770856FAc184B185878",
};
Updated Typed Data
Without Item Predicates
{
"LoanTerms": [
{
"name": "proratedInterestRate",
"type": "uint256"
},
{
"name": "principal",
"type": "uint256"
},
{
"name": "collateralAddress",
"type": "address"
},
{
"name": "durationSecs",
"type": "uint96"
},
{
"name": "collateralId",
"type": "uint256"
},
{
"name": "payableCurrency",
"type": "address"
},
{
"name": "deadline",
"type": "uint96"
},
{
"name": "affiliateCode",
"type": "bytes32"
},
{
"name": "nonce",
"type": "uint160"
},
{
"name": "side",
"type": "uint8"
}
]
}
With Item Predicates
{
"LoanTermsWithItems": [
{
"name": "proratedInterestRate",
"type": "uint256"
},
{
"name": "principal",
"type": "uint256"
},
{
"name": "collateralAddress",
"type": "address"
},
{
"name": "durationSecs",
"type": "uint96"
},
{
"name": "items",
"type": "Predicate[]"
},
{
"name": "payableCurrency",
"type": "address"
},
{
"name": "deadline",
"type": "uint96"
},
{
"name": "affiliateCode",
"type": "bytes32"
},
{
"name": "nonce",
"type": "uint160"
},
{
"name": "side",
"type": "uint8"
}
],
"Predicate": [
{
"name": "data",
"type": "bytes"
},
{
"name": "verifier",
"type": "address"
}
]
}
Collection Wide Offers
Single Asset
The third iteration of our protocol (V3) now supports Collection Wide Offers (CWOs) on single, unvaulted, assets. To create a CWO for a single NFT
- Set the NFT contract address on both the
collateralAddress
andcollectionId
fields - Set
collateralId
to-1
- Create and set
itemPredicates
const verifier = '0x1B6e58AaE43bFd2a435AA348F3328f3137DDA544'; // COLLECTION OFFER VERIFIER
const collection = '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d' // BAYC
// function to create itemPredicates for a collection offer
function createPredicate(collection) {
return {
verifier,
data: ethers.utils.defaultAbiCoder.encode(['address'], [collection]),
}
}
Example Terms
{
loanTerms:{
"durationSecs": 8640000,
"principal": "1000000",
"interestRate": "2000000000000000000000",
"collateralAddress": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
"collateralId": "-1",
"payableCurrency": "0x07865c6e87b9f70255377e024ace6630c1eaa37f",
"deadline": "1694375092",
"affiliateCode": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"collectionId": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
"signature": "0x99cd41a8af42772b6a34236b7da2f8a1d30d0cbcf83d1a64ac267ed58e800da214ee595029d927051551511e994bcac91951a755686197626a8e8fa6d50708101b",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "60",
"kind": "collection",
"role": "lender",
"itemPredicates": [
{
"verifier": "0x122a2d5D54A546708a80e7275b96D772f3826ffA",
"data": "0x0000000000000000000000003f228cbcec3ad130c45d21664f2c7f5b23130d23"
}
]
}
Vaulted Asset
The V2 protocol required assets to be vaulted in order to accept a CWO. There is a new VaultFactory address that must be used in the field collateralAddress
. You should still send the NFT contract address in collectionId
and provide itemPredicates
to scope your CWO to specific assets.
Example Terms
{
"durationSecs": 8640000,
"principal": "1000000",
"interestRate": "2000000000000000000000",
"collateralAddress": "0x269363665Dbb1582b143099a3cb467E98a476D55",
"collectionId": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
"collateralId": "-1",
"payableCurrency": "0x07865c6e87b9f70255377e024ace6630c1eaa37f",
"deadline": "1694375092",
"affiliateCode": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
Creating Item Predicates
Predicates scope CWO offers to a specific contract address and are verified by our verifying contract.
V3 Verifying Contract
0x1B6e58AaE43bFd2a435AA348F3328f3137DDA544
Example Code
const verifier = '0x1B6e58AaE43bFd2a435AA348F3328f3137DDA544';
const collection = '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d' // BAYC
function createPredicate(collection) {
return {
verifier,
data: ethers.utils.defaultAbiCoder.encode(['address'], [collection]),
}
}
Example Item Predicates
[
{
"verifier": "0x122a2d5D54A546708a80e7275b96D772f3826ffA",
"data": "0x0000000000000000000000003f228cbcec3ad130c45d21664f2c7f5b23130d23"
}
]
FAQ
Does Arcade support EIP-1271: Standard Signature Validation Method for Contracts?
- Yes - when submitting bids via the Arcade API populate the
fundingAddress
field with the contract address. Please ensure it has approved the Origination Controller with the bid's ERC-20 payable currency.