NAV
JS

Getting Started

Introduction

Welcome to Spectral, the leading provider of on-chain risk insight! This developer documentation will touch on our available forms of delivery, our products, and how to get started today!

If you have any questions regarding any of our products, get in touch with us!

Version: 0.1.0

Integrations Overview

There are three methods to integrate Spectral's risk insights into your application.

Scoracle Contract

The Scoracle Contract is a Solidity smart contract (the on-chain Chainlink oracle contract) that pulls the MACRO score from Spectral's Chainlink node.

Scoracle Contract Quickstart

To start the development with Spectral's Scoracle Contract:

  1. Follow the Prerequisites To Interacting With The Scoracle Contract section.
  2. Check out the Scoracle Example GitHub project. You can use it as a skeleton for your app.

Prerequisites To Interacting With The Scoracle Contract

To request a score from the Scoracle Contract:

  1. Arbitrum Goerli Test network must be available in your crypto wallet, such as Metamask. To add Arbitrum Goerli Test network to your wallet, visit Arbitrum Goerli Explorer, scroll to the bottom of the page, and click on Add Arbitrum One Testnet Network on the right side of the footer.
  2. Bridge testnet ETH and LINK from Goerli to Arbitrum Goerli using Arbitrum's Bridge.
  3. You must have access to the Arbitrum Goerli RPC endpoint on Alchemy or Infura.

To see a practical example, check Scoracle Example GitHub project.

Scoracle Contract Variables

Scoracle Contract Address

Contract Name Network ChainID Address
Scoracle.sol Arbitrum Goerli 421613 0xe953f329041dA0D5Cf23159abc4b45f6fbf8Ab17

Score Type ID

Score Type ID (Chainlink's externalJobID)
9348f5ba3f574f65958a675e468da9c8

Scoracle.sol - Integration

To integrate the MACRO score into your smart contract using Spectral's Scoracle Contract:

  1. Create IScoracle interface.
  2. Import the IScoracle interface and use it in your project.
  3. Obtain user signature.
  4. Prepare Score Type Job ID.
  5. Interact with your smart contract.

Scoracle.sol - Create IScoracle Interface

./contracts/interfaces/IScoracle.sol
pragma solidity 0.8.11;

interface IScoracle {
    struct ScoreData {
        uint40 lastUpdated;
        uint216 score;
        bytes extraData;
    }

    //===============Events===============

    /**
     * @dev emitted upon callback function of chainlink call. emits the address that was updated, the time it was updatedand the tick that was returned
     * @param addressToScore The address whose score is being updated
     * @param lastUpdated Timestamp of last update
     * @param score The new score
     * @param extraData Extra data to the type of score request
     **/
    event ScoreUpdated(address indexed addressToScore, uint256 lastUpdated, uint256 score, bytes extraData);

    /**
     * @dev Added a new score type
     * @param scoreTypeJobId The new adapter job id
     * @param scoreTypeName The new score type name
     **/
    event ScoreTypeAdded(bytes32 indexed scoreTypeJobId, string scoreTypeName);

    /**
     * @dev Deactivated a score type
     * @param scoreTypeJobId The new adapter job id
     **/
    event ScoreTypeDeactivated(bytes32 indexed scoreTypeJobId);

    /**
     * @dev Updated the chainlink node address
     * @param chainlinkNode The new chainlink node address
     **/
    event ChainlinkNodeUpdated(address indexed chainlinkNode);

    /**
     * @dev Updated the chainlink oracle address
     * @param chainlinkOracle The new chainlink oracle address
     **/
    event ChainlinkOracleUpdated(address indexed chainlinkOracle);

    /**
     * @dev Updated the base fee
     * @param baseFee Base fee updated
     **/
    event BaseFeeUpdated(uint256 baseFee);

    /**
     * @dev Updated the chainlink fee
     * @param chainlinkFee The new chainlink fee
     **/
    event ChainlinkFeeUpdated(uint256 chainlinkFee);

    //===============Main Functions===============

    function scoreRequest(
        address addressToScore,
        bytes32 _scoreTypeJobId,
        bytes memory _userSignature
    ) external payable;

    //===============Governance/Admin Functions===============

    function addScoreType(bytes32 _scoreTypeJobId, string memory _scoreTypeName) external;

    function deactivateScoreType(bytes32 _scoreTypeJobId) external;

    function updateChainlinkNode(address chainlinkNode) external;

    function updateChainlinkOracle(address chainlinkOracle) external;

    function updateBaseFee(uint256 baseFee) external;

    function updateChainlinkFee(uint256 chainlinkFee) external;

    function depositLINK(uint256 amount) external;

    function withdrawLINK(uint256 amount) external;

    function withdrawETH(uint256 amount) external;

    //===============Get Functions===============

    function getScore(address _user, bytes32 _scoreTypeJobId) external view returns (ScoreData memory scoreData);

    function checkScoreTypeExists(bytes32 _scoreTypeJobId)
        external
        view
        returns (
            bool,
            string memory,
            bytes32
        );

    function getChainlinkNode() external view returns (address);

    function getChainlinkOracle() external view returns (address);

    function getBaseFee() external view returns (uint256);

    function getChainlinkFee() external view returns (uint256);

    function getScoreBounds() external view returns (uint256, uint256);
}

Create a file called IScoracle.sol in the contracts/interfaces folder of your repository. Copy and paste the following code into the IScoracle.sol file.

Scoracle.sol - Import The IScoracle Interface

./contracts/MyContract.sol
pragma solidity 0.8.11;

import "./interfaces/IScoracle.sol";

contract MyContract {

    // The Deployed Scoracle.sol contract address on Arbitrum Goerli
    address constant SCORACLE_ADDRESS = 0xe953f329041dA0D5Cf23159abc4b45f6fbf8Ab17;

    constructor() {}

    // calcualateMacroScore - The function will (re)calculate a user's score by making a score request to the Scoracle contract
    // The Scoracle contract will then make request to the Chainlink Node where the score request will be fulfilled.
    function calculateMacroScore(
        bytes32 _scoreTypeJobId,
        bytes memory _userSignature
    ) external {
        IScoracle scoracle = IScoracle(SCORACLE_ADDRESS);
        scoracle.scoreRequest(msg.sender, _scoreTypeJobId, _userSignature);
    }

    // prequalifyUser - The function reads the user's score from the Scoracl contract's state and will prequalify the user for X depending on their score.
    function prequalifyUser(
        bytes32 _scoreTypeJobId
    ) public view returns (bool prequalified, uint256 score) {

        IScoracle scoracle = IScoracle(SCORACLE_ADDRESS);

        // Scoracle's getScore will read an already calculated score from the Scoracle contract's state.
        IScoracle.ScoreData memory scoreData = scoracle.getScore(msg.sender, _scoreTypeJobId);

        prequalified = (scoreData.score > 650) ? true : false;

        return (prequalified, scoreData.score);
    }
}

Import the IScoracle.sol interface into your smart contract. Then, create an instance of the Scoracle Contract using the IScoracle interface and deployed Scoracle Contract address.

With an instance of the Scoracle Contract in your code, you can now call the external functions in the Scoracle Contract to request a MACRO score for a particular address.

Scoracle.sol - Obtain User Signature

./helpers/utils.ts
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import * as ethers from 'ethers';

export const signScoracleMessage = (
  account: SignerWithAddress,
  scoracleAddress: string,
  chainId: number,
  nonce: number
) => {
  return account.signMessage(
    ethers.utils.arrayify(
      ethers.utils.solidityKeccak256(
        ['address', 'address', 'uint256', 'uint256'],
        [account.address, scoracleAddress, chainId, nonce]
      )
    )
  );
};

To request the Scoracle Contract to calculate MACRO score (scoreRequest() function), the user will first need to sign the message to obtain their signature.

Create a file ./helpers/utils.ts with the following content.

Scoracle.sol - Prepare Score Type Job ID

./helpers/utils.ts
import * as ethers from 'ethers';

export const signScoracleMessage = (
  (...)
)

// https://github.com/ethers-io/ethers.js/issues/66#issuecomment-370121220
export const stringToBytes32 = (text: string) => {
  let data = ethers.utils.toUtf8Bytes(text);
  if (data.length > 32) {
    throw new Error('too long');
  }
  data = ethers.utils.zeroPad(data, 32);
  return ethers.utils.hexlify(data);
};

You will need to submit the Score Type Job ID (_scoreTypeJobId parameter) to scoreRequest() function. Score Type Job ID must be converted from string to bytes32.

Add the following content to ./helpers/utils.ts.

Scoracle.sol - Interact With MyContract.sol

const SCORE_TYPE_ID = '9348f5ba3f574f65958a675e468da9c8';
const DeFiScoreIdType = stringToBytes32(SCORE_TYPE_ID);
const dummy_address = "0x000000000000000000000000000000000000dEaD";
const SCORACLE_ADDRESS = "0xe953f329041dA0D5Cf23159abc4b45f6fbf8Ab17";
const CHAIN_ID = 421613;
// Remember that the Scoracle.sol contract keeps track of used nonces for each user!
// After every request, the nonce must be incremented. 
let nonce = 0;
const accountSignature = await signScoracleMessage(dummy_address, SCORACLE_ADDRESS, CHAIN_ID, nonce);

// make request
await myContract.connect(accounts[0]).calculateMacroScore(DeFiScoreIdType, accountSignature);

Once you have all the helper functions created, you can use them to generate the arguments required to interact with the contract functions in MyContract.sol.

Remember that the Scoracle.sol contract keeps track of used nonces for each user. So you will need to manage nonces when making requests. After every request, the nonce must be incremented.

REST API

The Spectral REST API is a bridge for partners to pull risk insights directly into their application.

REST API Quickstart

To start the development with Spectral's REST API:

  1. Visit Spectral's Partner Dashboard to generate an API Key.
  2. Use generated API Key to authenticate API requests as described in the Authentication section.
  3. Trigger MACRO score calculation for a wallet address as described in the Calculate MACRO Score section.
  4. Read the calculated MACRO score with additional data as described in the Get MACRO Score section.

Spectral's URLs

Spectral's URL is the base URL for making API requests to the REST API for pulling data directly into your applications.

REST API URL

https://api.spectral.finance

REST API Authentication

Sample HTTP Request

GET /api/v1/addresses/0x573d19B66Cdc33f7E751f2a478ECeCe95155e798
Host: https://api.spectral.finance
Authorization: Bearer SFMYYYY.g2kfhsjAACRjNjliZWIwZS1jYTc1LTRjODUtOWVlMy01YThiZmI0MTAxNTVuBgCAlkjahsdlkfAFiAAFRgA.iEtc7xPGEemU-Z63sDQSTNmVFzCogdPycG7sAUK7p9k...

All requests to the REST API have to be authenticated with an API key (bearer access token). To get an API Key, visit Spectral's Partner Dashboard.!

Include the token in the HTTP Authorization header of your REST requests as a bearer token:

Authorization: Bearer <token>

REST API Endpoints

Calculate MACRO Score

   curl -i -X POST \
   'https://api.spectral.finance/api/v1/addresses/{wallet_address}/calculate_score'

Description: Initiates calculation of a wallet holder's score.

HTTP Request

POST /api/v1/addresses/{wallet_address}/calculate_score

Parameters

Name Located in Description Required Type
wallet_address path Wallet Address Yes string

Responses

Code Description
200 MACRO score calculation has been successfully scheduled.

Get MACRO Score

   curl -i -X GET \
   'https://api.spectral.finance/api/v1/addresses/{wallet_address}'

Sample response

   {
    "score": "350.00",
    "score_ingredients": {
      "credit_mix": 2,
      "defi_actions": 3,
      "health_and_risk": 4,
      "liquidation": 5,
      "market": 1,
      "time": 6,
      "wallet": 7
    },
    "score_timestamp": "2019-08-24T14:15:22Z",
    "probability_of_liquidation": "72.52",
    "risk_level": "HIGH_RISK",
    "wallet_address": "0xb4537b5bDfF24F757b939E27510b107BE2Ad473C"
   }

Description: Requests the pre-calculated MACRO score of a given wallet holder. If score_timestamp isn't up-to-date, the response might not be the most accurate. Specifically, the risk_level or probability_of_liquidation may be null. To get updated values, initiate the score calculation.

HTTP Request

GET /api/v1/addresses/{wallet_address}

Parameters

Name Located in Description Required Type
wallet_address path Wallet Address Yes string

Responses

Code Description
200 Wallet Details

List Wallets

   curl -i -X GET \
   'https://api.spectral.finance/api/v1/addresses'

Sample response

   [{
      "score": "350.00",
      "score_ingredients": {
        "credit_mix": 2,
        "defi_actions": 3,
        "health_and_risk": 4,
        "liquidation": 5,
        "market": 1,
        "time": 6,
        "wallet": 7
      },
      "score_timestamp": "2019-08-24T14:15:22Z",
      "probability_of_liquidation": "72.52",
      "risk_level": "HIGH_RISK",
      "wallet_address": "0xb4537b5bDfF24F757b939E27510b107BE2Ad473C"
    },
    {
      "score": "740.00",
      "score_ingredients": {
        "credit_mix": 2,
        "defi_actions": 6,
        "health_and_risk": 4,
        "liquidation": 5,
        "market": 1,
        "time": 3,
        "wallet": 7
      },
      "score_timestamp": "2019-08-24T14:11:22Z",
      "probability_of_liquidation": "39.42",
      "risk_level": "VERY_LOW_RISK",
      "wallet_address": "0xD7A556117f38c352a466F3DF33229a0ff8C4FC66"
   }]

Description: Requests information about all the wallets associated with the integration. Please note that only addresses that were associated using SDK integration are returned.

HTTP Request

GET /api/v1/addresses

Responses

Code Description
200 Returns an array of Wallet Details

Get Wallet Signals

   curl -i -X GET \
   'https://api.spectral.finance/api/v1/addresses/{wallet_address}/insights'

Sample response

  {
    "borrow": {
        "sum_borrows": 0.0,
        "total_auc_eth": 0.0,
        "total_borrows": 0,
        "total_current_loan_eth": 0.0,
        "total_interest_paid": 0.0,
        "total_repays": 0,
        "total_time_in_ever": 0
    },
    "credit_mix": {
        "count": 1,
        "count_borrow": 0,
        "count_lending": 1,
        "max_borrow_concentration": 0,
        "max_lending_concentration": 1.0
    },
    "lending": {
        "sum_redeems": 0,
        "total_auc_eth": 1.9312721017163819,
        "total_deposits": 7,
        "total_interest_earned": 0.0,
        "total_time_in_ever": 116996692
    },
    "liquidation": {
        "time_since_last_liquidated": 999999999,
        "total_amount_eth": 0.0,
        "total_liquidations": 0
    },
    "misc": {
        "available_borrow_amount_eth": 0.0,
        "average_available_borrow_amount_eth": 0.0,
        "average_collateral_balance_eth": 9.302608521938171e-4,
        "total_collateral_balance_eth": 7.153189427197098e-6
    },
    "overview": {
        "current_risk_factor": 0
    },
    "risk_factor": {
        "avg_risk_factor": 0.0,
        "counts_above_threshold": 0,
        "max_risk_factor": 0.0,
        "weighted_avg_risk_factor": 0.0
    }
  }

Description: Requests the wallet signals of a given wallet holder.

HTTP Request

GET /api/v1/addresses/{wallet_address}/insights

Parameters

Name Located in Description Required Type
wallet_address path Wallet Address Yes

Responses

Code Description
200 Wallet Insights

REST API Objects

Wallet Details

Attribute Type Format Description
score string Number MACRO score value.
score_ingredients object Score Ingredients Additional ingredients of the MACRO score.
score_timestamp string Date Time Timestamp at which the score has been calculated.
probability_of_liquidation string Number Probability of liquidation in the upcoming 60 days after score_timestamp.
risk_level string Describes the level of risk associated with the wallet. Possible values are: VERY_HIGH_RISK, HIGH_RISK, MEDIUM_RISK, LOW_RISK, VERY_LOW_RISK.
wallet_address string Ethereum wallet address

Score Ingredients

Each score ingredient is valued from [1-7], representing its effect on a user's score - with 1 having the highest negative impact and 7 the most positively supporting the score. A score ingredient of 0 means the user doesn't have enough activities.

Attribute Type Format Description
credit_mix integer Consideration of the number of DeFi lending protocols interacted with to assess the protocol concentration risk.
defi_actions integer Assessment of the various transactions undertaken on the DeFi lending protocols, e.g. borrowing, repayments, deposits, etc.
health_and_risk integer Evaluation of the amount of headroom maintained as part of your borrowing activities, similar to LTV.
liquidation integer Consideration of any historical liquidation events triggered on any of the bundled wallets.
market integer Evaluation of the general market volatility at the time of one's on-chain activities.
time integer Analysis of various time-based factors, e.g. length of wallet history.
wallet integer Assessment of the trend of wallet balance, transactions, and its composition.

Wallet Insights

Concepts:
* risk factor = 1 / health factor
* health factor = sum(collateral provided in ETH per token * liquidation threshold per token * usage as collateral enabled) / outstanding loan amount in ETH

Feature Group Attribute Type Format Description
overview current_risk_factor float Current risk factor at the time of borrow current* borrow event (transactions on current timestamp not inclusive) where risk factor at the current timestamp = 1 / Health factor.
borrow total_borrows int Count of all borrow transactions undertaken by a user until current timestamp.
borrow total_repays int Count of all repay transactions undertaken by a user up to current timestamp.
borrow total_time_in_ever int Total time (in seconds) for which a wallet had an outstanding borrow balance (total borrows - total repayments) until current timestamp.
borrow total_current_loan_eth float Current outstanding borrow balance in ETH at current timestamp, where: Borrow balance = (total borrows - total repays).
borrow total_interest_paid float Excess total amount repaid over amounts borrowed (in ETH) up to a current timestamp.
borrow total_auc_eth float Area Under Curve (AUC) in days and denominated in ETH of the historical daily borrow balances (net amount outstanding) up to a given timestamp (score timestamp), where AUC is the sum of the area of all rectangles at each historical day.
borrow sum_borrows float Sum of all borrow transaction amounts (in ETH) undertaken by a user up to current timestamp.`
lending total_deposits int Count all deposit transactions undertaken by a user up to current timestamp.
lending total_time_in_ever int Total time (in seconds) for which a wallet had a net positive lending balance (total deposits - total redeems) until current timestamp.
lending total_interest_earned float Difference (in ETH) between amounts deposited and redeemed up to current timestamp.
lending total_auc_eth float Area Under Curve (AUC) in days and denominated in ETH of historical daily net lending balances (lending amount outstanding) up to current timestamp, where AUC is sum of the area of all rectangles at each historical day.
lending sum_redeems float Sum of all redeem transaction amounts (in ETH) undertaken by a user up to current timestamp.`
liquidation total_liquidations int Number of liquidations of the wallet until current timestamp. Note: The origination fee liquidations in Aave are not counted.
liquidation total_amount_eth float Total amount a user has been liquidated for (in ETH) up to current timestamp.
liquidation time_since_last_liquidated int Time difference between current timestamp and the latest liquidation event before the given timestamp. The time is in seconds.
risk_factor max_risk_factor float Maximum of the historical daily risk factors starting from the first borrow date of the user, capped by maximum value of 1000000.`
risk_factor avg_risk_factor float Average daily historical risk factor from the first borrow date of the user, capped by the maximum value of 1000000.
risk_factor weighted_avg_risk_factor float Weighted average (by the day-end outstanding borrow amount) daily historical Risk Factor since the user’s first borrow transaction.
risk_factor counts_above_threshold int Number of times (days) the risk factor was greater than the predetermined threshold in the past (starting from the first borrow date of the user). Default threshold = 1 / 1.1.
credit_mix count int Count of DeFi lending protocols (current max value can be 2, i.e., Aave & Compound - will increase as and when we add other protocols) with which the user has ever made any type of DeFi transaction (lending, redeem, borrow, repay) in the past until current timestamp.
credit_mix count_borrow int Count of DeFi lending protocols (same caveat as above) from which the user has ever borrowed in the past until current timestamp.
credit_mix count_lending int Count of DeFi lending protocols (same caveat as above) to which the user has ever lent in the past until current timestamp.
credit_mix max_lending_concentration float Max concentration percentage of lending in one protocol (the percentage of the lending balance in the protocol, where the user has most lending volume).
credit_mix max_borrow_concentration float Max concentration percentage of borrowing from one protocol (the percentage of the borrow balance in the protocol, where the user has most borrowing volume).
misc total_collateral_balance_eth float Collateral balance deposited (in ETH) at current timestamp, where collateral balance is the sum of all deposit balances that the user has enabled to be used as collateral.
misc available_borrow_amount_eth float Amount (in ETH) a user is able to borrow at current timestamp, calculated as: sum(collateral deposited in ETH per token * liquidation threshold per token * usage as collateral enabled) - outstanding borrow balance in ETH at the timestamp.
misc average_available_borrow_amount_eth float Average of historical amounts (in ETH) a user can borrow until current timestamp that are day-end amounts available to borrow starting from the first transaction day in protocols until current day.
misc average_collateral_balance_eth float Average collateral balance deposited (in ETH) up to current timestamp where the historical collateral balances are day-end collateral amounts starting from the first transaction until current day.

SDK

Overview

The Spectral SDK provides additional integration of Spectral MACRO score into React applications. The SDK communicates with Spectral's APIs to retrieve information on risk insights.

After integrating with the SDK, you will get additional insights about users that computed Spectral's MACRO score using your application.

DEMO PAGE

Spectral Modal Video

Prerequisites

Partner ID

Access to the SDK requires a Partner ID. To get a Partner ID, visit Spectral's Partner Dashboard.

SDK Installation

In order to install the SDK run

npm install @spectral-finance/spectral-modal

or

yarn add @spectral-finance/spectral-modal

SpectralProvider Usage

import { SpectralProvider } from "@spectral-finance/spectral-modal";

export const App = () => {
  return (
    <SpectralProvider logo="yourLogoImageURL" partnerId="yourPartnerId">
        // Your App or Router Component
        {...}
    </SpectralProvider>
  );
};
  1. Import SpectralProvider and wrap it around the root app component.
  2. Use the logo parameter to apply a custom logo.
  3. Use the partnerID parameter to verify your Partner ID.

Calculate MACRO Score From Within Your Application

import { useSpectral } from "@spectral-finance/spectral-modal";

export const Home = () => {
  const { start, score } = useSpectral();
  const [myScore, setMyScore] = useState();

  useEffect(() => {
    if (!score) {
      console.log("Score not calculated");
      return;
    }
    console.log(`Hooray! your score is ${score}`);
    setMyScore(score);
  }, [score]);

  return (
    <div>
      {/* Opens the Spectral Modal and starts the bundle and
          calculation process */}
      <button type="button" onClick={start}>
        Calculate Spectral Score
      </button>
    </div>
  );
};

The useSpectral hook can be used anywhere inside the app context. Once the MACRO score is calculated it will be served within the score key provided by the hook.

JS