@predictdotfun/sdk
v1.2.0
Published
A TypeScript SDK to help developers interface with the Predict's protocol.
Downloads
494
Readme
A TypeScript SDK to help developers interface with the Predict's protocol.
Install
This package has ethers v6 as a peer dependency.
yarn add @predictdotfun/sdk ethers
npm install @predictdotfun/sdk ethers
See the OrderBuilder
class for more in-depth details on each function.
Set Approvals
Before trading, you need to set approvals for ERC-1155 (ConditionalTokens
) and ERC-20 (USDB
). This can be achieved by sending a transaction to the respective contracts (see the Contracts section) and approving both the CTF_EXCHANGE
and the NEG_RISK_CTF_EXCHANGE
or via the SDK utils.
Contracts: The current deployed contracts can be found either in the Constants.ts
file or in the Deployed Contracts documentation.
The following example demonstrates how to set the necessary approvals using the SDK utils.
import { Wallet, MaxInt256 } from "ethers";
import { OrderBuilder, ChainId, Side } from "@predictdotfun/sdk";
// Create a wallet to sent the approvals transactions (must be the orders' `maker`)
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function which initiates the OrderBuilder (only once per signer) and then provides it as dependency to other util functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
/**
* NOTE: To use your predict account, export the Privy wallet from the account settings at https://predict.fun/account/settings
*
* You will need to deposit ETH on that address (the Privy wallet) to set approvals and cancel orders.
*
* Then initiate the `OrderBuilder` as such:
*
* const builder = await OrderBuilder.make(ChainId.BlastMainnet, privyWallet, { predictAccount: "PREDICT_ACCOUNT_ADDRESS" });
*
* Replacing `PREDICT_ACCOUNT_ADDRESS` with your Predict account address/deposit address.
*/
// Call an helper function to set the approvals and provide the OrderBuilder instance.
await setApprovals(builder);
}
async function setApprovals(builder: OrderBuilder) {
// Set all the approval needed within the protocol
const result = await builder.setApprovals();
// Check if the approvals were set successfully
if (!result.success) {
throw new Error("Failed to set approvals.");
}
}
How to use a Predict account
Here's an example of how to use a Predict account to create/cancel orders and set approvals.
- Initiate the Privy Wallet: The wallet is needed to sign orders. Can be found in the account settings.
- Ensure the Privy Wallet has funds: You will need to add some ETH to be able to set approvals and cancel orders, if needed.
- Initialize
OrderBuilder
: Instantiate theOrderBuilder
class by callingOrderBuilder.make
.- NOTE: Include the
predictAccount
address, which is also known as the deposit address.
- NOTE: Include the
- Set Approvals: Ensure the necessary approvals are set (refer to Set Approvals).
- Determine Order Amounts: Use
getLimitOrderAmounts
to calculate order amounts. - Build Order: Use
buildOrder
to generate aLIMIT
strategy order.- NOTE: Fetch the
feeRateBps
via theGET /markets
endpoint on the REST API - NOTE: Set the
signer
andmaker
to thepredictAccount
address, NOT the signer/privy wallet address.
- NOTE: Fetch the
- Generate Typed Data: Call
buildTypedData
to generate typed data for the order. - Sign Order: Obtain a
SignedOrder
object by callingsignTypedDataOrder
. - Compute Order Hash: Compute the order hash using
buildTypedDataHash
.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId, Side } from "@predictdotfun/sdk";
// Export and fund with ETH the Privy wallet from the account settings at https://predict.fun/account/settings
const privyWallet = new Wallet(process.env.PRIVY_WALLET_PRIVATE_KEY);
// The main function which initiates the OrderBuilder (only once per signer) and then provides it as dependency to other util functions
async function main() {
/**
* NOTE: Replace `PREDICT_ACCOUNT_ADDRESS` with your Predict account address/deposit address.
*/
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, privyWallet, {
predictAccount: "PREDICT_ACCOUNT_ADDRESS",
});
// Call an helper function to create the order and provide the OrderBuilder instance
await createOrder(builder);
}
async function createOrder(builder: OrderBuilder) {
// Step 1. Set approvals and define the order params as usual
// Step 2. Create the order by using the Predict account address as both the `signer` and `maker`
const order = builder.buildOrder("LIMIT", {
maker: "PREDICT_ACCOUNT_ADDRESS",
signer: "PREDICT_ACCOUNT_ADDRESS",
side: Side.BUY, // Equivalent to 0
tokenId: "OUTCOME_ON_CHAIN_ID", // This can be fetched via the API or on-chain
makerAmount, // 0.4 USDB * 10 shares (in wei)
takerAmount, // 10 shares (in wei)
nonce: 0n,
feeRateBps: 0, // Should be fetched via the `GET /markets` endpoint
});
// Step 3. Sign and submit the order as usual
}
Limit Orders
Here's an example of how to use the OrderBuilder to create and sign a LIMIT
strategy buy order:
- Create Wallet: The wallet is needed to sign orders.
- Initialize
OrderBuilder
: Instantiate theOrderBuilder
class by callingOrderBuilder.make
. - Set Approvals: Ensure the necessary approvals are set (refer to Set Approvals).
- Determine Order Amounts: Use
getLimitOrderAmounts
to calculate order amounts. - Build Order: Use
buildOrder
to generate aLIMIT
strategy order.- NOTE: Fetch the
feeRateBps
via theGET /markets
endpoint on the REST API
- NOTE: Fetch the
- Generate Typed Data: Call
buildTypedData
to generate typed data for the order. - Sign Order: Obtain a
SignedOrder
object by callingsignTypedDataOrder
. - Compute Order Hash: Compute the order hash using
buildTypedDataHash
.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId, Side } from "@predictdotfun/sdk";
// Create a wallet for signing orders
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function which initiates the OrderBuilder (only once per signer) and then provides it as dependency to other util functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
/**
* NOTE: To use your predict account, export the Privy wallet from the account settings at https://predict.fun/account/settings
*
* You will need to deposit ETH on that address (the Privy wallet) to set approvals and cancel orders.
*
* Then initiate the `OrderBuilder` as such:
*
* const builder = await OrderBuilder.make(ChainId.BlastMainnet, privyWallet, { predictAccount: "PREDICT_ACCOUNT_ADDRESS" });
*
* Replacing `PREDICT_ACCOUNT_ADDRESS` with your Predict account address/deposit address.
*/
// Call an helper function to create the order and provide the OrderBuilder instance
await createOrder(builder);
}
async function createOrder(builder: OrderBuilder) {
/**
* NOTE: You can also call `setApprovals` once per wallet.
*/
// Set all the approval needed within the protocol (if needed)
const result = await builder.setApprovals();
// Check if the approvals were set successfully
if (!result.success) {
throw new Error("Failed to set approvals.");
}
// Simple helper function to calculate the amounts for a `LIMIT` order
const { pricePerShare, makerAmount, takerAmount } = builder.getLimitOrderAmounts({
side: Side.BUY,
pricePerShareWei: 400000000000000000n, // 0.4 USDB (in wei)
quantityWei: 10000000000000000000n, // 10 shares (in wei)
});
// TODO: If you are using your Predict account (deposit address) use that address, otherwise use the signer address
const predictAccountOrSigner = "PREDICT_ACCOUNT_ADDRESS" || signer.address;
// Build a limit order
const order = builder.buildOrder("LIMIT", {
maker: predictAccountOrSigner,
signer: predictAccountOrSigner,
side: Side.BUY, // Equivalent to 0
tokenId: "OUTCOME_ON_CHAIN_ID", // This can be fetched via the API or on-chain
makerAmount, // 0.4 USDB * 10 shares (in wei)
takerAmount, // 10 shares (in wei)
nonce: 0n,
feeRateBps: 0, // Should be fetched via the `GET /markets` endpoint
});
// Build typed data for the order (isNegRisk can be fetched via the API)
const typedData = builder.buildTypedData(order, { isNegRisk: true });
// Sign the order by providing the typedData of the order
const signedOrder = await builder.signTypedDataOrder(typedData);
// Compute the order's hash
const hash = builder.buildTypedDataHash(typedData);
// Example structure required to create an order via Predict's API
const createOrderBody = {
data: {
order: { ...signedOrder, hash },
pricePerShare,
strategy: "LIMIT",
},
};
}
Market Orders
Similarly to the above, here's the flow to create a MARKET
sell order:
- Create Wallet: The wallet is needed to sign orders.
- Initialize
OrderBuilder
: Instantiate theOrderBuilder
class by callingOrderBuilder.make
. - Set Approvals: Ensure the necessary approvals are set (refer to Set Approvals).
- Fetch Orderbook: Query the Predict API for the latest orderbook for the market.
- Determine Order Amounts: Use
getMarketOrderAmounts
to calculate order amounts. - Build Order: Call
buildOrder
to generate aMARKET
strategy order.- NOTE: Fetch the
feeRateBps
via theGET /markets
endpoint on the REST API
- NOTE: Fetch the
- Generate Typed Data: Use
buildTypedData
to create typed data for the order. - Sign Order: Obtain a
SignedOrder
object by callingsignTypedDataOrder
. - Compute Order Hash: Compute the order hash using
buildTypedDataHash
.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId, Side } from "@predictdotfun/sdk";
// Create a wallet for signing orders
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function which initiates the OrderBuilder (only once per signer) and then provides it as dependency to other util functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
/**
* NOTE: To use your predict account, export the Privy wallet from the account settings at https://predict.fun/account/settings
*
* You will need to deposit ETH on that address (the Privy wallet) to set approvals and cancel orders.
*
* Then initiate the `OrderBuilder` as such:
*
* const builder = await OrderBuilder.make(ChainId.BlastMainnet, privyWallet, { predictAccount: "PREDICT_ACCOUNT_ADDRESS" });
*
* Replacing `PREDICT_ACCOUNT_ADDRESS` with your Predict account address/deposit address.
*/
// Call an helper function to create the order and provide the OrderBuilder instance
await createOrder(builder);
}
async function createOrder(builder: OrderBuilder) {
// Fetch the orderbook for the specific market via `GET orderbook/{marketId}`
const book = {};
/**
* NOTE: You can also call `setApprovals` once per wallet.
*/
// Set all the approval needed within the protocol (if needed)
const result = await builder.setApprovals();
// Check if the approvals were set successfully
if (!result.success) {
throw new Error("Failed to set approvals.");
}
// Helper function to calculate the amounts for a `MARKET` order
const { pricePerShare, makerAmount, takerAmount } = builder.getMarketOrderAmounts(
{
side: Side.SELL,
quantityWei: 10000000000000000000n, // 10 shares (in wei) e.g. parseEther("10")
},
book, // It's recommended to re-fetch the orderbook regularly to avoid issues
);
// TODO: If you are using your Predict account (deposit address) use that address, otherwise use the signer address.
const predictAccountOrSigner = "PREDICT_ACCOUNT_ADDRESS" || signer.address;
// Build a limit order
const order = builder.buildOrder("MARKET", {
maker: predictAccountOrSigner,
signer: predictAccountOrSigner,
side: Side.SELL, // Equivalent to 1
tokenId: "OUTCOME_ON_CHAIN_ID", // This can be fetched via the API or on-chain
makerAmount, // 10 shares (in wei)
takerAmount, // 0.4 USDB * 10 shares (in wei)
nonce: 0n,
feeRateBps: 0, // Should be fetched via the `GET /markets` endpoint
});
// Build typed data for the order (isNegRisk can be fetched via the API)
const typedData = builder.buildTypedData(order, { isNegRisk: false });
// Sign the order by providing the typedData of the order
const signedOrder = await builder.signTypedDataOrder(typedData);
// Compute the order's hash
const hash = builder.buildTypedDataHash(typedData);
// Example structure required to create an order via Predict's API
const createOrderBody = {
data: {
order: { ...signedOrder, hash },
pricePerShare,
strategy: "MARKET",
slippageBps: "2000", // Only used for `MARKET` orders, in this example it's 0.2%
},
};
}
Redeem Positions
The OrderBuilder
class provides methods to redeem your positions on the Predict protocol. Depending on the type of market you're interacting with, you can use either redeemPositions
for standard markets or redeemNegRiskPositions
for NegRisk markets.
- Create a Wallet: Initialize a wallet that will be used to sign the redemption transaction.
- Initialize
OrderBuilder
: Instantiate theOrderBuilder
class by calling the staticmake
method. - Redeem Positions: Call the
redeemPositions
method with the appropriateconditionId
andindexSet
.
The conditionId
and indexSet
can be fetched from the GET /positions
endpoint.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId } from "@predictdotfun/sdk";
// Initialize the wallet with your private key
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function initiates the OrderBuilder (only once per signer)
// Then provides it as dependency to other functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
await redeemPositions(builder);
await redeemNegRiskPositions(builder);
}
async function redeemPositions(orderBuilder: OrderBuilder) {
const conditionId = "CONDITION_ID_FROM_API"; // A hash string
const indexSet = "INDEX_SET_FROM_API"; // 1 or 2 based on the position you want to redeem
const result = await builder.redeemPositions(conditionId, indexSet);
if (result.success) {
console.log("Positions redeemed successfully:", result.receipt);
} else {
console.error("Failed to redeem positions:", result.cause);
}
}
async function redeemNegRiskPositions(orderBuilder: OrderBuilder) {
const conditionId = "CONDITION_ID_FROM_API"; // A hash string
const indexSet = "INDEX_SET_FROM_API"; // 1 or 2 based on the position you want to redeem
const amount = "POSITION_AMOUNT_FROM_API"; // The amount to redeem, usually the max
const result = await builder.redeemNegRiskPositions(conditionId, indexSet, amount);
if (result.success) {
console.log("Positions redeemed successfully:", result.receipt);
} else {
console.error("Failed to redeem positions:", result.cause);
}
}
Check USDB balance
The method balanceOf
allows to easily check the current USDB balance of the connected signer.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId } from "@predictdotfun/sdk";
// Initialize the wallet with your private key
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function initiates the OrderBuilder (only once per signer)
// Then provides it as dependency to other functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
await checkBalance(builder);
}
async function checkBalance(orderBuilder: OrderBuilder) {
// Fetch the current account/wallet balance in wei
const balanceWei = await builder.balanceOf();
// Example check
if (balanceWei >= orderAmountWei) {
console.log("Enough balance to create the order");
} else {
console.error("Not enough balance to create the order");
}
}
Contracts
To facilitate interactions with Predict's contracts we expose the necessary instances of each contract, including ABIs and types.
import { OrderBuilder } from "@predictdotfun/sdk";
import { Wallet } from "ethers";
// Create a wallet to interact with on-chain contracts
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY);
// The main function initiates the OrderBuilder (only once per signer)
// Then provides it as dependency to other functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
await callContracts(builder);
}
async function callContracts(orderBuilder: OrderBuilder) {
// If the signer is not provided within `OrderBuilder.make` the contracts won't be initiated
if (!orderBuilder.contracts) {
throw new Error("The signer was not provided during the OrderBuilder init.");
}
// You can now call contract functions (the actual contract instance is within `contract`)
// The `codec` property contains the ethers Interface, useful to encode and decode data
const balance = await orderBuilder.contracts["USDB"].contract.balanceOf(signer.address);
}
Other utils
Some other useful utils, ABIs and types exposed by the SDK.
import {
// Supported Chains
ChainId,
// Addresses
AddressesByChainId,
// Contract Interfaces
BlastCTFExchange,
BlastConditionalTokens,
BlastNegRiskAdapter,
BlastNegRiskCtfExchange,
ERC20,
// ABIs
BlastCTFExchangeAbi,
BlastNegRiskCtfExchangeAbi,
BlastNegRiskAdapterAbi,
BlastConditionalTokensAbi,
ERC20Abi,
// Order builder
OrderBuilder,
} from "@predictdotfun/sdk";
Cancel Orders
Here's an example on how to cancel orders via the SDK
- Fetch Orders: Retrieve your open orders using
GET /orders
. - Cancel Orders off chain: Call
POST /orders/cancel
with orderIds and cancel orders from the orderbook - Group by
isNegRisk
: Separate orders based on theisNegRisk
property. - Cancel Orders: Call the specific cancel function based on the order(s) type (
isNegRisk
). - Check Transaction Success: Check to confirm the transaction was successful.
import { Wallet } from "ethers";
import { OrderBuilder, ChainId, Side } from "@predictdotfun/sdk";
// Create a new JsonRpcProvider instance
const provider = new JsonRpcProvider(process.env.RPC_PROVIDER_URL);
// Create a wallet to send the cancel transactions on-chain
const signer = new Wallet(process.env.WALLET_PRIVATE_KEY).connect(provider);
// The main function which initiates the OrderBuilder (only once per signer) and then provides it as dependency to other util functions
async function main() {
// Create a new instance of the OrderBuilder class. Note: This should only be done once per signer
const builder = await OrderBuilder.make(ChainId.BlastMainnet, signer);
/**
* NOTE: To use your predict account, export the Privy wallet from the account settings at https://predict.fun/account/settings
*
* You will need to deposit ETH on that address (the Privy wallet) to set approvals and cancel orders.
*
* Then initiate the `OrderBuilder` as such:
*
* const builder = await OrderBuilder.make(ChainId.BlastMainnet, privyWallet, { predictAccount: "PREDICT_ACCOUNT_ADDRESS" });
*
* Replacing `PREDICT_ACCOUNT_ADDRESS` with your Predict account address/deposit address.
*/
// Call an helper function to cancel orders and provide the OrderBuilder instance
await createOrder(builder);
}
async function cancelOrders(builder: OrderBuilder) {
// Fetch your open orders from the `GET /orders` endpoint
const apiResponse = [
// There are more fields, but for cancellations we only care about `order` and `isNegRisk`
{ order: {}, isNegRisk: true },
{ order: {}, isNegRisk: false },
{ order: {}, isNegRisk: false },
];
// Determine which orders you want to cancel
const ordersToCancel = [
{ order: {}, isNegRisk: true },
{ order: {}, isNegRisk: false },
];
const regularOrders: Order[] = [];
const negRiskOrders: Order[] = [];
// Group the orders by `isNegRisk`
for (const { order, isNegRisk } of ordersToCancel) {
if (isNegRisk) {
negRiskOrders.push(order);
} else {
regularOrders.push(order);
}
}
// Call the respective cancel functions
const regResult = await builder.cancelOrders(regularOrders);
const negRiskResult = await builder.cancelNegRiskOrders(regularOrders);
// Check for the transactions success
const success = regResult.success && negRiskResult.success;
}
License
By contributing to this project, you agree that your contributions will be licensed under the project's MIT License.