blueprints.js
blueprints.js is a free and open-source TypeScript library for working with Blueprints. It can be installed from npm.
npm i @blueprintsfi/blueprints.js
Note that blueprints.js does not provide functionality for interacting with the smart contracts directly.
Defining tokens
import { Basket, Blueprint, Fraction, MapBytes, NativeToken, WithMetadata } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
// Represents the chain's native token (e.g. ETH).
const eth = new NativeToken(9n, {
decimals: 18n,
});
assert.strictEqual(eth.toString(), "9 NativeToken<18>");
// Tokens can be also parsed from their string representations. This
// position represents an ERC20 token with address
// `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` that has 18 decimals.
const erc20 = Blueprint.fromString(
"4.5 ERC20Wrapper<0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 18>",
);
assert.strictEqual(erc20.params.address, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
assert.strictEqual(erc20.params.decimals, 18n);
// Represents a basket – a single token that wraps two positions inside.
const basket = new Basket(new Fraction(2n, 3n), {
tokens: [eth, erc20],
});
assert.strictEqual(basket.toString(), "~0.666666666666666666667 Basket<[9 NativeToken<18>, 4.5 ERC20Wrapper<0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 18>]>");
// Attaches application-specific bytes to a token without changing its token id.
const annotated = new WithMetadata(2n, {
token: new NativeToken(1n, { decimals: 18n }),
metadata: new MapBytes({
user_comment: "I know I'm overexposed but the price is down right now",
}),
});
assert.strictEqual(
annotated.toString(),
`2 WithMetadata<1 NativeToken<18>, {"user_comment":"I know I'm overexposed but the price is down right now"}>`,
);
assert.strictEqual(annotated.externalTokenId().toHex(), annotated.params.token.externalTokenId().toHex());
Each Blueprint has its own docs page, where you can learn about its parameters. The token parameters are immutable, unlike its amount.
yesToken, noToken
yesToken and noToken are helper functions for creating
ComposablePrediction tokens with a single oracle, whose response is binary.
import {
yesToken,
noToken,
ERC20Wrapper,
ComposablePrediction,
MapBytes,
MultisigOracle,
HashedBytes,
} from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const oracle = new MultisigOracle({
addresses: ["0xCA2421bE4AA793c9F95faef57811F4eD360f54e1"],
threshold: 1n,
feedId: new HashedBytes(new MapBytes({
market: "BTC",
condition: "above 100000",
time: "2027-01-01 00:00:00 UTC",
})),
});
const collateral = new ERC20Wrapper(20n, {
decimals: 18n,
address: "0x6fc2e9b25fa19eeC97a05A18aF69332477BA3728",
});
const yes = yesToken(collateral, oracle);
assert(yes instanceof ComposablePrediction);
// This is equivalent to:
const yes2 = new ComposablePrediction(1n, {
collateral,
constraints: [
{ oracle, startRange: 0n, endRange: 1n },
],
});
assert.strictEqual(yes.externalTokenId().toHex(), yes2.externalTokenId().toHex());
const no = noToken(collateral, oracle);
assert(no instanceof ComposablePrediction);
// This is equivalent to:
const no2 = new ComposablePrediction(1n, {
collateral,
constraints: [
{ oracle, startRange: 1n, endRange: 0n },
],
});
assert.strictEqual(no.externalTokenId().toHex(), no2.externalTokenId().toHex());
String representation
Each token has exactly one string representation. It can be obtained by calling
the toString method. A token can be parsed from its string
representation using the Blueprint.fromString
function.
Tokens' amounts in their string representations might be approximated, as seen
in the basket defined in the defining tokens section.
However, if that is the case, enough decimals will be included so that the exact
amount can be recovered unambiguously.
toString()
Returns the token's unique string representation.
It optionally takes a single format parameter which can be raw or html. If
it is omitted or set to raw, the raw string representation is returned. If set
to html, the string representation is formatted as HTML, which means parts of
it are wrapped in <span> tags with different classes:
bp-address– for address literals,bp-int– for integer literals,bp-bytes– for bytes literals,bp-string– for string literals,bp-func– for function names,bp-amount– for token amounts,bp-blueprint– for blueprint names,bp-oracle– for oracle names.
import { Basket, ERC20Wrapper, Fraction, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = new NativeToken(9n, {
decimals: 18n,
});
assert.strictEqual(eth.toString(), "9 NativeToken<18>");
assert.strictEqual(
eth.toString("html"),
`<span class="bp-amount">9</span> <span class="bp-blueprint">NativeToken</span><<span class="bp-int">18</span>>`,
);
Blueprint.fromString()
Parses a token from its unique string representation. Throws a SyntaxError if
an invalid representation is passed.
import { Blueprint, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = Blueprint.fromString("9 NativeToken<18>");
assert(eth instanceof NativeToken);
assert.strictEqual(eth.params.decimals, 18n);
params
Token's params. It has the same type as the second argument of the Blueprint's constructor.
import { Blueprint } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const erc20 = Blueprint.fromString("10 ERC20Wrapper<0x0ABb8fFa90597b00a3E1927d78dbaBe23573ED12, 18>");
assert.strictEqual(erc20.params.address, "0x0ABb8fFa90597b00a3E1927d78dbaBe23573ED12");
assert.strictEqual(erc20.params.decimals, 18n);
Fraction
Fraction is a helper class which has two read-only attributes – numerator
and denominator. Both of them are bigints. The numerator has to be
non-negative and the denominator has to be positive. Each fraction has a
unique strict representation among fractions with the same denominators that
can be obtained using the toString method. Fraction can be parsed from its
string representation using the Fraction.fromString function which takes
the desired fraction denominator.
import { Fraction } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const half = new Fraction(2n, 4n);
assert.strictEqual(half.numerator, 2n);
assert.strictEqual(half.denominator, 4n);
assert.strictEqual(half.toString(), "0.5");
const third = new Fraction(1n, 3n);
assert.strictEqual(third.toString(), "~0.333");
const zero = new Fraction(0n, 1n);
assert.strictEqual(zero.toString(), "0");
const twoThirds = Fraction.fromString("~0.667", 3n);
assert.strictEqual(twoThirds.numerator, 2n);
assert.strictEqual(twoThirds.denominator, 3n);
Multiplier
Each token has a multiplier which depends on its parameters (but not the
amount). It is the number of wei tokens a single token can be split into. For
example, the only parameter of NativeToken<18> indicates that it has 18
decimals and a multiplier of .
A multiplier doesn't have to be a power of 10. For an example, take a look at
the basket token defined in the defining tokens section,
which has a multiplier of .
amount
A Fraction. The numerator is equal to the amount in wei and the denominator
is equal to the token multiplier. Note that this implies that all tokens
created by a single Blueprint with the same parameters have the same
amount.denominator.
Note that if the denominator of the Fraction passed to the Blueprint's
constructor or to the setAmount method isn't equal
to the multiplier, the fraction will be automatically multiplied. However,
an error will be thrown if the exact desired amount cannot be expressed.
import { Fraction, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = new NativeToken(new Fraction(1n, 2n), {
decimals: 18n,
});
assert.strictEqual(eth.amount.numerator, 5n * 10n ** 17n);
assert.strictEqual(eth.amount.denominator, 10n ** 18n);
assert.strictEqual(eth.amount.toString(), "0.5");
eth.setWeiAmount(2n * 10n ** 18n);
assert.strictEqual(eth.amount.numerator, 2n * 10n ** 18n);
assert.strictEqual(eth.amount.denominator, 10n ** 18n);
assert.strictEqual(eth.amount.toString(), "2");
weiAmount
A shortcut for amount.numerator.
multiplier
A shortcut for amount.denominator.
setAmount()
Sets token's amount. Takes a Fraction or a bigint.
import { Fraction, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = new NativeToken(1n, {
decimals: 18n,
});
eth.setAmount(new Fraction(1n, 2n));
assert.strictEqual(eth.amount.numerator, 5n * 10n ** 17n);
assert.strictEqual(eth.amount.denominator, 10n ** 18n);
eth.setAmount(0n);
assert.strictEqual(eth.amount.numerator, 0n);
assert.strictEqual(eth.amount.denominator, 10n ** 18n);
setWeiAmount()
Takes a single bigint – the new token amount in wei.
import { Fraction, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = new NativeToken(1n, {
decimals: 18n,
});
eth.setWeiAmount(2n * 10n ** 18n);
assert.strictEqual(eth.amount.numerator, 2n * 10n ** 18n);
assert.strictEqual(eth.amount.denominator, 10n ** 18n);
assert.strictEqual(eth.amount.toString(), "2");
internalTokenId()
Returns the token's internal id as an Uint8Array of length 32.
externalTokenId()
Returns the token's external id as an Uint8Array of length 32.
blueprintAddress()
Returns the Blueprint's smart contract address.
copy()
Copies the token.
import { ERC20Wrapper } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const eth = new ERC20Wrapper(20n, {
address: "0xF85C1127D6157993d492EBEcE36a3b820DAF8fF7",
decimals: 18n,
});
const eth2 = eth.copy();
assert(eth2 instanceof ERC20Wrapper);
// Since token's params are immutable, the `params` object is the same (a copy
// is not made).
assert(eth.params === eth2.params);
eth2.setAmount(40n);
assert.strictEqual(eth2.amount.toString(), "40");
// Original token stays untouched.
assert.strictEqual(eth.amount.toString(), "20");
Bytes
Some blueprints and oracles take bytes as one of their parameters. Often the
bytes have some special interpretation. For example, a feed id of a
MultisigOracle might be a hash of a string. This should be expressed in the
oracle's string representation. To achieve this, those blueprints and oracles
take a Bytes object. It is an abstract class that has several subclasses, each
of them corresponding to a different interpretation.
RawBytes
Represents bytes that have no special meaning. The constructor takes either a
hex-encoded buffer as string (without 0x prefix) or Uint8Array.
import { MultisigOracle, RawBytes } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const feedId = "e53dcc3bd5f13fc11bfa1907103c388b67d7b6018e4c301aa61b3cdafb1452af";
const oracle = new MultisigOracle({
addresses: ["0xCA2421bE4AA793c9F95faef57811F4eD360f54e1"],
threshold: 1n,
feedId: new RawBytes(feedId), // `Uint8Array.fromHex(feedId)` also works
});
assert.strictEqual(oracle.toString(), "MultisigOracle<[0xCA2421bE4AA793c9F95faef57811F4eD360f54e1], 1, 0xe53dcc3bd5f13fc11bfa1907103c388b67d7b6018e4c301aa61b3cdafb1452af>");
StringBytes
Represents bytes that should be interpreted as a string. The string can contain
only printable ASCII characters (including space) other than " and \.
import { HashedBytes, MultisigOracle, StringBytes } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const feedId = "e53dcc3bd5f13fc11bfa1907103c388b67d7b6018e4c301aa61b3cdafb1452af";
const oracle = new MultisigOracle({
addresses: ["0xCA2421bE4AA793c9F95faef57811F4eD360f54e1"],
threshold: 1n,
feedId: new HashedBytes(new StringBytes("bitcoin above 100000 on 2027-01-01 00:00:00 UTC")),
});
assert.strictEqual(
oracle.toString(),
`MultisigOracle<[0xCA2421bE4AA793c9F95faef57811F4eD360f54e1], 1, keccak256("bitcoin above 100000 on 2027-01-01 00:00:00 UTC")>`,
);
MapBytes
Represents bytes that should be interpreted as a JSON-encoded string-to-string
map. Keys and values can contain only printable ASCII characters (including
space) other than " and \.
import { MapBytes, WithMetadata, NativeToken } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const metadata = new MapBytes({
user_comment: "I know I'm overexposed but the price is down right now",
});
const token = new WithMetadata(2n, {
token: new NativeToken(1n, { decimals: 18n }),
metadata,
});
assert.strictEqual(
metadata.toString(),
`{"user_comment":"I know I'm overexposed but the price is down right now"}`,
);
assert.strictEqual(
token.toString(),
`2 WithMetadata<1 NativeToken<18>, {"user_comment":"I know I'm overexposed but the price is down right now"}>`,
);
HashedBytes
Represents bytes that are Keccak-256 hash of some other bytes. The constructor
takes another Bytes object.
import { HashedBytes, MapBytes, MultisigOracle } from "@blueprintsfi/blueprints.js";
import assert from "node:assert";
const feedId = "e53dcc3bd5f13fc11bfa1907103c388b67d7b6018e4c301aa61b3cdafb1452af";
const oracle = new MultisigOracle({
addresses: ["0xCA2421bE4AA793c9F95faef57811F4eD360f54e1"],
threshold: 1n,
feedId: new HashedBytes(new MapBytes({
market: "BTC",
condition: "above 100000",
time: "2027-01-01 00:00:00 UTC",
})),
});
assert.strictEqual(
oracle.toString(),
`MultisigOracle<[0xCA2421bE4AA793c9F95faef57811F4eD360f54e1], 1, keccak256({"market":"BTC","condition":"above 100000","time":"2027-01-01 00:00:00 UTC"})>`,
);