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 to integrate MACRO score with Solidity smart contracts.
- REST API to get MACRO score via HTTP. This method also provides additional wallet insights.
- SDK to integrate MACRO score into React applications.
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:
- Follow the Prerequisites To Interacting With The Scoracle Contract section.
- 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:
- 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. - Bridge testnet ETH and LINK from Goerli to Arbitrum Goerli using Arbitrum's Bridge.
- 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:
- Create
IScoracle
interface. - Import the
IScoracle
interface and use it in your project. - Obtain user signature.
- Prepare Score Type Job ID.
- 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:
- Visit Spectral's Partner Dashboard to generate an API Key.
- Use generated API Key to authenticate API requests as described in the Authentication section.
- Trigger MACRO score calculation for a wallet address as described in the Calculate MACRO Score section.
- 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.
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>
);
};
- Import
SpectralProvider
and wrap it around the root app component. - Use the
logo
parameter to apply a custom logo. - 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.