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 of body.

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 and collectionId 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.
Language
Click Try It! to start a request and see the response here!