NAV Navbar

Introduction

Run is a platform to build apps and tokens on Bitcoin. Developers create applications using interactive objects, called jigs. Jigs allow you to build almost anything you can dream up: tokens, contracts, interfaces, elections, games, digital pets, and more โ€” all run on Bitcoin.

Run supports full object-oriented programming and arbitrary code today. It is secure and performant for real applications. These docs will help you get started!

Getting Started

Installation

Load both bsv and Run in the browser

<script src="bsv.browser.min.js"></script>
<script src="run.browser.min.js"></script>

Load Run in Node.js

const Run = require('run-sdk')

If you're new to Run, let the tutorial series guide your journey to get acquainted. You can write code without installing anything. The web browser Console will be your playground.

The details written here in the Docs have example code in the sidebar on the right-hand side. Anything you read in paragraphs, you can preview in-action over there. โ‡ฅ

Run works everywhere including all major browsers, on desktop and mobile, as well as Node.js 10+ on servers. Run is written in JavaScript ES6 and uses the bsv library to build and sign transactions. To get started:

And that's it. All your code and jigs will be saved on-chain and Run will use public APIs to interact with the Bitcoin network. You don't need to deploy any servers with Run. All the logic works client-side.

Setup

const run = new Run({ network: 'mock' })

The Run instance manages your communication with the Bitcoin network. The default network is main (Mainnet), but for development and testing, we recommend mock. Mock is an in-memory simulation blockchain that does not require funds to use. We like to call it the mockchain. For more configuration options, see API Reference: Run.

Creating Jigs

class SimpleStore extends Jig {
  set(value) {
    this.value = value
  }
}

const jig = new SimpleStore()

jig.set('Satoshi Nakamoto')

await jig.sync()

console.log(jig.owner)
console.log(jig.location)
console.log(jig.origin)

Let's begin with a basic jig that stores a value in a variable. Create a jig called SimpleStore. By extending from Jig, the instances of your class will automatically sync to the blockchain. Every jig has an owner. Any code may read jigs but only its owner can update it. The owner is typically a Bitcoin address, and the private key is required to update it.

In addition to owner, every jig has a location. A jig's location is the pairing of a Bitcoin transaction ID and an output index, and it represents a particular state in time of an object or class on Bitcoin. When you check the location property of a jig, you get its current location. If you wish to get the location where the jig was first deployed, that is called its origin. The origin is unique and will not change, however location changes with every update. After a method call, your jig will have a new location pointing to a Bitcoin transaction containing the last method call.

Loading Jigs

Loading all jigs

await run.inventory.sync()

const simpleStore = run.inventory.jigs.find(x => x instanceof SimpleStore)

simpleStore.set(123)

Loading a specific jig

const specificJig = await run.load(simpleStore.location)

specificJig.set('abc')

// Fast-forward the jig to its latest state
await specificJig.sync()

The simplest way to load your jigs is to call run.inventory.sync() and then access run.inventory.jigs. run.inventory.sync() will find and load all objects owned by run.owner and place them in the jigs array. Once loaded, you may call methods and use them normally. Alternatively, you may wish to load a particular jig or load a jig in a historical state. To do either, pass the location of the jig you wish to load into run.load().

If you've loaded a historical location so that your jig is in a previous state, you'll need to first catch up to the latest state before you'll be allowed to make a method call. sync() will handily fast-forward a jig to its latest state on the blockchain without triggering a Bitcoin transaction. In the example on the sidebar, if specificJig was in a historical state, you would call specificJig.sync() and then call specificJig.set('abc'). When you accidentally try to update a jig without the jig being in its latest state, Run will safely abort before publishing a Bitcoin transaction and inform you with an error. In that case, you'll just need to add the preceding sync() call and execute your code again. The best practice is to write code in such a way that jigs are always up-to-date in their latest state. Run manages the heavy lifting for you.

sync() also acts as a debugging tool since it surfaces any errors your jigs have after you've made changes to them. If you notice your app acting funny, a well-placed preceding specificJig.sync() can help you uncover the error. You may also call sync() on the Run instance, like this: run.sync(). That'll help you search out any errors from your entire app in all of the jigs you own.

Jigs

Jigs are interactive objects on Bitcoin. You define a jig with a JavaScript class and that class determines exactly what the jig can do. Every instance of a jig is unique. Each one has an owner and only that owner can update the jig. How is that secured? Bitcoin! Let's explore how you create a jig.

Creating

class Post extends Jig { 
    init(message) {
        this.message = message
    }
}

new Post('Hello, world')

Let's create a new jig class called Post for represents a comment on a message board. In JavaScript, your constructor is called constructor but for jigs, this method is called init. Think of them the same way. If init throws an exception, the jig will never be created, just like constructors. You create jigs with the new keyword, just as you would with normal JavaScript objects, and they get deployed onto the Bitcoin network. Pretty cool.

Updating

class EditablePost extends Post {
    edit(message) {
        this.message = message
    }
}

const editablePost = new EditablePost('Hello, world')

editablePost.edit('Hello, BitCoin')

Jigs are updated by calling methods. In fact, this is the only way to update jigs. Your jig class defines the ways that your jig instances may evolve, so be sure to think ahead. When you call a method, Run publishes a Bitcoin transaction with data in an op_return that includes the method name and its arguments. The state may be recomputed simply by playing back every update one-by-one. For more information about how it works, see How It Works.

Sending

class Dragon extends Jig {
  send(to) {
    this.owner = to
  }
}

new Dragon().send(address)

Jigs may be sent to someone else by changing the owner property of a jig in a method. You can set the owner to a Bitcoin address, a public key, or a custom Lock. The new owner will be able to update the jig starting in the next transaction.

Syncing

Wait for updates to complete

class LoyaltyCard extends Jig {
    init() { this.stamps = 0 }
    stamp() { this.stamps +=1 }
}

const card = new LoyaltyCard()
await card.sync()

card.stamp()
await card.sync()

Sync a jig from its origin to its latest state

const card2 = await run.load(card.origin)

await card2.sync()

Run is asynchronous. When you create or update jigs, Run creates Bitcoin transactions for you in the background and broadcasts them to the network. As with any network request, the request may sometimes fail. Your connection may go down, or a node on the network may reject your transaction.

Run updates your jig's state with each method call you make. However, it does not continuously update your jigs with the blockchain. Because if it did, as a developer you'd feel like you were on shifting sand. Instead, you've got a reliable and steady state for each jig that you're working with. Therefore, depending on the app you've built and actions of the owner of the jig, you might need to catch up a jig to its latest state because it could have been modified outside of your app.

Every jig has sync() available to ensure your local state is the same as that on the network. Calling await jig.sync() returns when:

  1. All pending local transactions have been published successfully, and
  2. The jig has been caught up with any new transactions from the network.

Any errors in validation or network requests will throw an error.

When testing ideas out in the browser, you won't need to call sync() very much. Run will happily execute method calls on jigs in their latest state. However, in a production app, the best practice is to call sync() after every method call to ensure that you catch errors early and handle them gracefully. Calling sync() will force any error to show up at that particular line in the code.

Interactivity

class Event extends Jig {
  createTicket() { return new Ticket(this) }
}

class Ticket extends Jig {
  init(event) { this.event = event }
}

Event.deps = { Ticket }

Jigs are designed to interact together. Jigs may create other jigs, call methods on other jigs, read properties on other jigs, and even store other jigs inside them. These are just a few of the types of interactions that Run supports.

Calling new to construct a jig within a method of another jig will create a new output in the transaction. Sometimes you will create new jigs that are of the same class, but other times you will want to create jigs that are of a different class. Because Jig code runs in a sandbox without access to other code, you set the deps property on your jig class to tell Run about any other classes you use. These dependencies will be made available as globals to your jig.

Jigs may store other jigs as properties too. Think of these as standard JavaScript object references that you may read or write. However, if stored jigs are updated by calling methods, then their owners must also sign the Bitcoin transaction that updates their state. For more information about when owners need to approve, see the Ownership Rules.

Destroying

Destroy a jig

giftCard.destroy()

Override destroy to finlize a jig

class GiftCard extends Jig {
  init(value) {
    this.value = value
  }

  destroy() {
    super.destroy()
    this.value = 0
  }
}

Not all jigs will live forever. You may want to delete jigs you no longer need, and or consume jigs within other jigs. Calling destroy() on the jig creates a new transaction that removes the jig's output and makes it un-updatable.

Destroyed jigs still have locations for their last state. They can even still be loaded using run.load. However, destroyed jigs can no longer be updated and they are marked as being destroyed with a null owner. They are forever locked in their final state. The locations for destroyed jigs are the pairing of its final transaction ID and a unique index for that destroyed jig.

By default, every jig has a destroy method. If you wish to prevent a jig from being destroyed, override the destroy method and throw an error in it. You can call destroy from outside the jig or from another method. You can also override destroy to put the jig into its final state, as seen to the right.

Backing

class Tip extends Jig {
  init(message, pubkey, amount) {
    this.message = message
    this.owner = pubkey
    this.satoshis = amount
  }

  withdraw() {
    this.satoshis = 0
  }
}

new Tip('I like your videos', pubkey, 100000)

Jigs may be backed by bitcoins. When a jig is backed, it means that the output associated with that jig has a non-dust value in it. Backed jigs let users send money between each other and provide a baseline value for items. To back a jig, set the satoshis property to a number. Your purse will automatically deposit that amount into the jig. When the satoshis property is later decreased, those Bitcoins will be withdrawn to the current Run purse.

It is important to remember that backed jigs may be melted at any time by spending the jig output outside of Run. This will destroy the jig, and the owner will keep the satoshis.

Checking Parameters

Attaching an item

class Hat extends Jig { }

class Person extends Jig {
  wear(item) {
    expect(item).toBeInstanceOf(Hat)

    this.item = item
  }
}

Person.deps = { Hat, expect: Run.extra.expect }

You should always check your parameters when you write jig methods. If you are expecting a number, make sure to check for a number. If you are expecting a jig, then make sure you receive a jig of the right kind. This prevents people from mis-using your jigs.

For your convenience, Run provides the expect sidekick. expect offers assertions similar to the ones you might be familiar with in Jest or Chai. It lets you to check many things, including if a jig is a certain class, if a number is greater than another number, or if a parameter is of a certain type. To get started, add expect as a dependency to your jig class as seen to the right. Then, check out the expect documention to review the available methods.

Sometimes you may wish to check if a class is part of a changing set. For example, a game may have a list of item classes that may change over time. This is a little more advanced. See the Dynamic Whitelists section.

Adding an Icon

const emoji = '๐Ÿฆ–'

const image = await Run.extra.B.loadWithMetadata('55e2c09672355e009e4727c2365fb61d12c69add91215ee3e9f50aa76c808536', {
  title: 'T Rex skull icon',
  license: '[CC BY 3.0](http://creativecommons.org/licenses/by/3.0)',
  author: '[Delapouite](https://delapouite.com/)',
  source: '[game-icons.net](https://game-icons.net/1x1/delapouite/t-rex-skull.html)'
})

class DinoPet extends Jig { }

DinoPet.metadata = { emoji, image }

new DinoPet()

You may optionally set an icon onto a jig using an emoji, custom image, or both. These icons will show in explorers, exchanges, and wallets, whenever your jig is displayed. To attach an icon, you set the metadata property on your jig class.

Setting an emoji: To add an emoji, set the emoji field on metadata to a single emoji character. You can find an appropriate emoji on Emojipedia.

Setting an image: To set an image, first upload your image on-chain. Bitcoin Files is an excellent tool for this. BitcoinFiles stores your image using a special transaction format for data called the B:// protocol. The B:// data you uploaded can then be loaded and set as an image using the B berry class as seen to the right. Simply set the berry loaded to the image field on metadata. Currently Run supports SVG and PNG formats. SVG is recommended in most cases because it scales up better on large displays and loads quicker due to its smaller file size.

Code

With Run, you can own code, like classes and functions, just as easily as you can own objects. This may sound a little meta at first, but don't worry โ€” you've been doing it all along! Every jig you created was linked to a jig class which you owned too. That jig class lived in its own output and had its own location.

When you own a piece of code, it means you have control over that behavior. For example, if you own a jig class, you get to decide what its jig instances are able to do. You can update or destroy that class and even call methods on it. Run calls classes and functions that people own Code with a capital C. Using Code, you can define new kinds of jigs, create reusable helper functions, share your server logic for auditability, discover algorithms from other apps, and much more.

There are two main kinds of Code in Run:

  1. Jig classes: Jig classes are classes that extend from Jig and behave like jigs. You already know these. You can use them to create jig objects as we've already seen, but you can also call methods on them to update their properties over time. Jig classes evolve according to same Ownership Rules as jig objects.

  2. Sidekicks: Sidekicks are your helpers. They are your classes or functions that don't extend from Jig which you use more for supporting roles. You might use sidekicks to create a custom lock, to define a berry, to make a shared helper like expect, or even to store your server code on-chain. When you call a method on a sidekick, nothing gets recorded to Bitcoin. But you can call sidekicks from inside jigs and even from your own app. There are no restrictions on what kinds of parameters can be passed into or returned from sidekicks.

Using jig classes and sidekicks, you have a powerful repertoire to build extensible apps.

Deploying

Deploy a sidekick function

function sha256(data) { ... }

const sha256Code = run.deploy(sha256)

await sha256Code.sync()

You deploy your classes and functions to Bitcoin. Deploying uploads your code in a Bitcoin transaction and creates a new output for it that you own. To deploy, you call run.deploy() and pass in your local class or function. Run will publish the transaction in the background and make you the code's owner. Being the owner allows you to perform actions using the code. Code is not necessarily static, as we'll see.

run.deploy() also returns to you a new copy of your class or function that you should use going forward. This copy is special: unlike your original code, this copy is linked to your new Bitcoin output. It is also securely sandboxed and acquires several special Code methods that you can use to update it. We suggest adding the word Code to the name of the copy to differentiate it from your local class. You can call sync() on it right away to make sure that it deployed successfully.

We recommend you deploy all of your code in advance and separately from your app. This way, your app and your users have a stable library to work with. You can either do this yourself using a script or give the Deploy Tool a try.

Loading

Load a Data class

const Data = await run.load('<class-location-goes-here>')

const data = new Data('abc')

Find a class you own in the inventory

await run.inventory.sync()

const MyClass = run.inventory.code.find(C => C.origin === myClassOrigin)

After you've deployed your code, you will need to load it again later in your application to use it. run.load(), in addition to loading jigs, is also able to download, install and sandbox your classes and functions from the blockchain too. Just pass in the location of the code you wish to download it. We recommend loading all of the code you use at the start of your app once.

Alternatively, you can use the inventory to find your code. run.inventory.sync() will load all the code that you own and place it in the run.inventory.code array. You can filter that array by origin to find the code you plan to use.

Linking

Deploy a jig class with a dependency

class Reward extends Jig { ... }

class LootBox extends Jig {
  init() { this.reward = new Reward() }
}

LootBox.deps = { Reward }

run.deploy(LootBox)

You can create code on Bitcoin that uses other code on Bitcoin, just like you would in your application. But there's one caveat: you have to tell Run whenever you use other classes or functions, because Run needs to be able to link the code together.

You tell Run about other code you use by setting the deps property on your class or function. The deps property should be set to an object having all of the code you use inside. Those dependencies stored in deps will be made available as globals inside your class methods or functions.

Extending

class EditablePost extends Post {
  edit(message) {
    this.message = message
  }
}

You can extend classes to add or change base class behavior. This is especially useful for jigs, when you might want to create a hierarchy of related types. For example, think of a game where a base Vehicle class is extended to create many types of vehicles, like planes and trains.

To extend a class, you use the extends keyword as you normally would in JavaScript. When you deploy your extension, Run will automatically create separate outputs for both the parent and the child, and also link them together. You do not need to add the parent class to the deps object on the child. Run is smart enough to automatically add the parent as a dependency.

Inside methods on the child class, you can access the parent class either by name or by using the super keyword in JavaScript as you normally would. This is often useful when you want to override a method on the parent to add behavior but you still want to call the parent method inside.

By default, extending a class requires the parent class's approval. This ensures the instanceof keyword is secure, because if Run didn't require parent class approval, third-parties could extend from a class to hijack instanceof checks. Sometimes, however, you want third-parties to extend from your classes. In those cases, set the sealed property on your class to false before you deploy and anyone will be able to create extensions.

Syncing

Load a class at its origin and sync to its latest state

const DigitalPet = await run.load('<digital-pet-class-origin>')

await DigitalPet.sync()

When you call MyClass.sync(), it works just like myJig.sync(). If there are pending transactions, they will be published to the blockchain, and if there are network updates you haven't received, Run will download and apply them. We always recommend calling sync() after every code update to catch errors too, just like jigs. If you load a class by its origin, it is best to call sync() right after to make sure you have the latest state. For more about syncing, see Jigs: Syncing.

Updating

class Weapon extends Jig {
  static setPower(power) {
    this.power = power
  }
}

const WeaponCode = run.deploy(Weapon)

WeaponCode.setPower(100)

// WeaponCode.power === 100

You can call methods on jig classes just like jig objects. Class methods can change properties, call other class methods, and create instances. You can even send the code to someone else by changing its owner!

To write a jig class method, put static before the method name and it will apply to the class itself and not instances. When you call a class method, Run publishes a Bitcoin transaction with the update and assigns the code to a new location. Other jigs that use the code will see the updates when they sync.

You should think of code as living objects with their own history. Although only jig classes can have static methods that update their state, all code including sidekicks can be upgraded, destroyed, and authed.

Upgrading

const Weapon = await run.load('<weapon-class-location>')

const NewWeapon = class Weapon {
  ...
}

Weapon.upgrade(NewWeapon)

await Weapon.sync()

After your code is deployed, you may discover that it is missing an important feature or that it has a bug. On other blockchains, this is a terminal failure! With Run, you can call upgrade() on your code and pass in a replacement class or function. That replacement will become the new code. You can use this to add new functionality, fix bugs, or change rules over time. Make sure that all methods and properties you want are still present on the new code, just as if you deployed it for the first time.

When other jigs or code that use your code are synced, your upgrades will be automatically applied. You cannot change past behavior though; you can only change the future.

If you wish to provably prevent upgrades, you can set upgradable to false on the class. This is useful if you wish to show others that your code will not fundamentally change.

With great power comes great responsibility! Be careful.

Tokens

Using jigs, you can create tokens similar to ERC-20. These can be used for stablecoins, in-game currencies, tradable reward points, shares of stock, and more. Unlike ERC-20 however, where different users interact through a smart contract, in Run each user owns their own tokens. This makes them more like native bitcoin.

In Run, tokens are just jigs that are fungible, meaning that you can divide them into smaller pieces and combine them back together, like cash. Tokens can be minted, owned, sent, and redeemed. And while you could write your own token class to implement this functionality, Run provides a base Token standard for your convenience. You extend from Token to create your own fungible token. As the owner of the class, only you will be able to mint new coins for your users.

Here we will guide you through a few use cases. For more, see API Reference: Token.

Defining

Deploy a new token class

const run = new Run({ owner })

// Define the token class
class USDCoin extends Token { }
USDCoin.decimals = 6
USDCoin.symbol = 'USDC'
USDCoin.metadata = { emoji: '๐Ÿ’ต' }
USDCoin.backingBank = 'HSBC'

// Deploy
run.deploy(USDCoin)
await run.sync()

// Write this down
console.log(USDCoin.location)

You can define your own tradable token by creating a class that extends from Token. Your extension uniquely identifies your token and it will have its own name and properties. You don't need to add any methods either โ€” all of the most common functionality for tokens is already in the base class. You will however probably want to set a few properties: like symbol, decimals, metadata, and anything particular to your token. Make sure to check out the Standard Metadata section for metadata used by wallets and exchanges.

Next, deploy your class. You may use ether the Deploy Tool or run.deploy() in a script. Be sure to write down its location afterward, and make sure you've deployed it using an appropriate owner. You mint new tokens through the class, so its owner should be very secure. You may assign your token class to a private key or a more complex ownership script. Consider assigning your class to a Group lock to require multiple parties to sign off for mints.

Minting

// Load and sync your token class
const USDCoin = await run.load(tokenClassLocation)
await USDCoin.sync()

// Mint new coins to users. Only USDCoin.owner may do this
const mintedCoin = USDCoin.mint(100, address)
await mintedCoin.sync()

console.log(mintedCoin.amount) // 100

Minting tokens is as simple as calling mint() on your token class. You pass in the amount of tokens to mint along with the address to mint to. If you don't specify an address, you will mint tokens to the class owner.

Every time you mint, your token class is spent and the class receives a new location. One best practice is to the save the location for your token class after every mint. This way, Run can load it quickly the next time. When you load the exact latest location of the class, Run can get it from its cache. When you load from an older location, sync() will take some time to catch up.

You can mint to several users in a single transaction. Just wrap all your mint() calls inside of a run.transaction().

Sending

Send some of a coin and keep the change

const sentCoin = coinToSend.send(pubkey, 30)

Send to two users in the same transaction

run.transaction(() => {
  coinToSend.send(savanna, 10)
  coinToSend.send(margot, 20)
})

await run.sync()

Users that own your tokens can freely send them to others. To send a token, simply call send() and pass in the new owner and the amount to send. If you don't specify an amount, then the whole token amount will be sent. The return value of send() is the new coin that was sent. The change will remain in the current token.

You can send to several users in a single transaction by wrapping all your send() calls inside of a run.transaction().

Combining

Combine three tokens together

firstToken.combine(secondToken, thirdToken)

Combine and send in a single transaction

const tokens = run.inventory.jigs.filter(jig => jig instanceof USDCoin)

const tx = new Run.Transaction()
tx.update(() => tokens[0].combine(...tokens.slice(1)))
tx.update(() => tokens[0].send(amount, address))
await tx.publish()

After users have received several tokens, each of the same class, they can convert them into a single token having the combined amount using the combine() method. This is like taking five twenty-dollar bills to the bank to receive one one-hundred dollar bill. To combine tokens, call the combine() method on one token and pass in the other tokens that should be merged into it.

It is best if apps and wallets combine tokens regularly to minimize the number of objects that need to be loaded. One best practice to to combine before every send. You can even combine and send in a single transaction, as seen over there. โ‡ฅ

Redeeming

Redeem a token as bitcoin

const sender = token.sender
const satoshiValue = tokensToSatoshis(token.amount)

class Payment extends Jig {
  init(owner, satoshis) {
    this.owner = owner
    this.satoshis = satoshis
  }
}

run.transaction(() => {
  token.destroy()
  new Payment(sender, satoshiValue)
})

await run.sync()

As an issuer, you may need to redeem tokens back for their underlying asset. For example, a stablecoin might be redeemed into its backing currency. Every token has a sender property that stores the last owner of that particular token. You can use this to determine who to redeem to.

Usually, you'd run a server that would monitor a redeem address. Users would send their tokens to that address, and when you receive a token, you'd use the sender property on it to send the underlying asset. Then you can destroy that token so that it is no longer in circulation.

Limiting Supply

class CollectableCoin extends Token {
  static mint(amount, to) {
    if (CollectableCoin.supply + amount > CollectableCoin.total) {
      throw new Error('Cannot mint: Max supply reached')
    }

    return super.mint(amount, to)
  }
}

CollectableCoin.total = 10000
CollectableCoin.upgradable = false

You may want to limit the supply of a token. You can call destroy() on your token class after you've minted all tokens. This will prevent any further mints. It's a good idea for when you don't know in advance how many tokens you will mint. Calling destroy also prevents new tokens from being created in your name even if your private owner key were to get hacked.

Another approach is to set a max supply for a token by overriding the mint() method to track the number of coins minted, as seen to the right. Notice that the upgradable property is set to false; this prevents upgrades to your class that would to remove this check. This creates a provably limited supply, and is a good idea for when you want to limit the supply in advance.

Blacklisting

You may need to blacklist individual tokens. For example, a user may lose their keys and request that you reissue their tokens. Or law enforcement may come to you to request blacklisting if your tokens are being used illegally. In both cases, the recommended solution is to keep a list of token locations that are blacklisted and not redeem any token that is on that blacklist. You will need to track descendant UTXOs too and blacklist them as well. But stay tuned! A simpler solution is coming.

Advanced Usage

Standard Metadata

class DigitalPet extends Jig { }

DigitalPet.metadata = {
  author: 'Maximus',
  license: 'MIT',
  emoji: '๐Ÿ‰'
}

run.deploy(DigitalPet)

Wallets, explorers, and exchanges will want to show your jigs. You can add special metadata to any creation to help these services do so. This metadata is stored as an object called metadata on your jig, code, or berry, and it contains properties that assist apps. The following properties are considered standard metadata:

Presentation

Property Type Description
name string String name to use in place of the class or function name
description string Short sentence, less than 80 characters, that describes the jig for users
emoji string Single UTF-8 emoji character to represent the jig
image B instance Reference to an SVG or PNG image stored using B://

Attribution and Licensing

Property Type Description
author string markdown Name of the creator for the code or content
title string markdown Title of the content
source string markdown URL where the content was found
license string markdown License for the code or content

You can start by setting these properties on your jig classes, as seen to the right. By convention, jig instances will automatically use the metadata from their class. However, jig instances may also have their own metadata that overrides its class metadata. You can put any information in metadata you deem important, even properties that are not listed above. More will become standard over time.

Batch Transactions

Use two jigs separately in one transaction

run.transaction(() => {
  new SomeJig()
  anotherJig.send(address)
})

await run.sync()

Multiple updates may be batched together in a single Bitcoin transaction. Batching makes the actions atomic, meaning all of them happen or none of them happen. This is great when two users want to exchange jigs, for example. If any errors occur while publishing the updates, all of the jig changes in the transaction will be reverted. Besides making updates atomic, batching also reduces fees.

Any number of updates may be batched together from different jigs. Call run.transaction() and pass in a callback function that includes all of the actions that you want batched together. Then call run.sync() to wait for the updates to publish. If you need more advanced transaction control, see the Transaction API

Owners must still sign off on any updates to a jig, so if you just created a jig in a new output, you can't yet call a method on it because its owner can't sign. If you just sent a jig to someone, they also can't use it until the next transaction.

Caller Variable

Storing the parent jig

class Child extends Jig {
  init() {
    this.parent = caller
  }
}

Enforcing a method may only be called by certain classes

class Database extends Jig {
  init(rootUser) {
    this.rootUser = rootUser
  }

  deleteAll() {
    if (caller !== this.rootUser) throw new Error('only the root user may delete the database')
  }
}

class User extends Jig {
  deleteDatabase(database) {
    database.deleteAll()
  }
}

Within code, there is a special caller variable that is available. This variable is always set to the jig or jig class that called the current method, or null if the current method was invoked from application code. Sidekicks are transparent and will never be a caller.

You can use caller to store a jig's creator, enforce that a jig may only be created by a specific parent, or create administrator jigs that uniquely can call special methods.

Private Properties

class TradingCard extends Jig {
  send(to) {
    this._checkNotBlacklisted(to)
    this.owner = to
  }

  _checkNotBlacklisted(owner) {
    if (TradingCard.blacklist.includes(owner)) {
      throw new Error('blacklisted')
    }
  }
}

TradingCard.blacklist = [cheaterAddress]

Jigs may have private methods and private properties.

Private methods are useful when you have internal helper methods that shouldn't be called from outside. To make a method private, you put an underscore before the method name. When a method is private, it can only be called by the current jig, a jig of the same class, or the jig class itself. If a different jig attempts to call a private method, Run will throw an error.

Run supports private properties too. Like methods, you put an underscore before the property name to make it private. You can use private properties to hide state that shouldn't be used by other jigs. Private properties may only be read from inside the current jig, from a jig of the same class, or from the class itself. If you try to access a private property from another class, Run will throw an error.

Auth Method

Limit the creation of jig instances by forcing the class to auth

class SingleTonMonster extends Jig {
  init() {
    SingleTonMonster.auth()
  }
}

Every jig and every code has an auth() method on it. To auth means to spend a jig, forcing its owner to sign off on any actions performed. Auth also ensures that the jig's state is the latest. You may use auth() for many purposes, like to have a class approve of the creation of instances, as seen to the right. You can also use it to get approval from several jigs before performing some action. For example, you may require that several players in a game auth using their characters for some shared action to take place, like moving on to the next round. Authing is a fundamental action that will become more useful to you over time.

Code Presets

class Dragon extends Jig {
  set(name) {
    this.name = name
  }
}

// Use the deploy tool to deploy the Dragon class and generate these presets

Dragon.presets = {
  main: {
    origin: 'bee45c75c37a289517f33ebfa051601c9610ccc56fbddfbabc44413db5b0bc1b_o1',
    location: 'bee45c75c37a289517f33ebfa051601c9610ccc56fbddfbabc44413db5b0bc1b_o1',
    nonce: 1,
    owner: '13amCautaFqwbWV6MoC86xrh96W4fXGfDV',
    satoshis: 0
  }
}

When you wish to create an instance of a jig from a class you've already deployed, you have two options. Either you can load the class from the blockchain via run.load() and then create an instance, or you can create an instance from a local class that has presets applied. When a local class has presets, Run doesn't have to download the code from the blockchain. Presets are a great way to share jig classes in NPM libraries.

You can use the Deploy Tool to generate presets. However, you can also create them manually. To do this, upload your code to each Bitcoin network you wish to support using run.deploy() in a script. The presets are origin, location, nonce, owner, and satoshis set onto a presets object for the given network. Run will detect these presets and automatically use the origins and locations on the appropriate network.

Berries

Load a metanet node

const txid = '2f24d7edb8de0ef534d8e0bc2413eddda451b4accc481519a1647d7af79d8e88'
const node = await MetanetNode.load(txid)

Define the MetanetNode Berry

class MetanetNode extends Berry {
  // Its constructor can only be called by its pluck function. Run guarantees this.
  init(pnode, parent, data) {
    this.pnode = pnode
    this.parent = parent
    this.data = data
  }

  // Run calls this pluck function when you call run.load with a Berry class.
  static async pluck(location, fetch) {
    const data = txo(await fetch(location))

    if (data.out[0].s1 === 'meta') {
      const pnode = data.out[0].s2
      const txidParent = data.out[0].s3
      const data = data.out[0].s4

      if (data === 'METANET_ROOT') {
        return new MetanetNode(pnode, null, data)
      } else {
        const parentNode = await MetanetNode.load(txidParent)
        return new MetanetNode(pnode, parentNode, data)
      }
    }
  }
}

MetanetNode.deps = { txo: Run.extra.txo }

Third-party protocols, like Twetch, B, and Metanet, may be used within Run via Berries. Berries are deterministic JavaScript objects that capture data from outside Run that's on the blockchain. If you can write code to parse an OP_RETURN, you can create Berries. Berries are more than data though. They are objects. They may have methods, their types can be checked, they may be passed into jigs, stored as properties, cached, and more.

To load berries using an existing Berry class, you call BerryClass.load() and pass in the path to load, which is often a transaction ID. Run ships with one berry class, B, that may be used to load B:// protocol data.

To define a new Berry protocol, you create a class that extends from the base Berry class and implement the static pluck() method. Run calls your pluck() method to load data for that path into a JavaScript object. The second parameter to pluck allows you to fetch transactions from the blockchain. Run fetches transaction data in raw hex format, but you can parse it using the txo or Tx helpers.

Once loaded, berries have locations just like jigs. Berry locations are slightly more advanced though and include the class location, the path, and more, in the form: <berry_class_location>?berry=<path>&hash=<state-hash>&version=<protocol>. This is a unique identifier that allows them to be safely referenced and cached.

Locks

Send a token to a P2PK output script

class PubkeyLock {
  constructor(pubkey) { this.pubkey = pubkey }
  script() { return asm(`${this.pubkey} OP_CHECKSIG`) }
  domain() { return 74 }
}

PubkeyLock.deps = { asm }

token.send(new PubkeyLock(pubkey))

A provably unspendable lock

class Unspendable {
  script() { return asm('OP_RETURN') }
  domain() { return 0 }
}

Unspendable.deps = { asm }

The default owner for a jig is a Bitcoin address. This is a great default, but sometimes you'll want more advanced ownership. For example, you may want a group to own a jig, so that no one party has exclusive control. Or you may wish for a jig to be provably owned by nobody. Both of these and more are possible with locks.

A lock is an object that produces a Bitcoin output script. You can set one as the owner on your jigs. Run ships with a Group lock class for multi-sig ownership, and you may also create your own locks by implementing the Lock API. Your lock class will have two methods: script() and domain(). After implementing these methods, you set the owner of a jig to an instance of your lock, and Run will produce an output with your script.

You'll probably also want to unlock your jigs to update them. To do that, create a corresponding key by implementing the Owner API. Your implementation of Owner will need to implement two methods: sign() and nextOwner().

Locks are deterministic, typed, and run in a sandbox environment. This means they can be safely used inside jigs. But this also means they can't use the bsv library or similar external code. Run provides an asm helper function to fill the gap when building custom scripts inside Lock classes.

Tips and Tricks

Limiting Supply

class Weapon extends Jig {
  init(owner) {
    // Only the GameItems class can mint Weapon instances
    expect(caller).toBe(GameItems)

    this.owner = owner
  }
}

class GameItems extends Jig {
  static createWeapon(owner) {
    this.weaponCount++

    if (this.weaponCount > 10) throw new Error('too many weapons')

    return new Weapon(owner)
  }
}

GameItems.weaponCount = 0

GameItems.deps = { Weapon, expect }
Weapon.deps = { GameItems, expect }

The supply of a jig class may be limited through the use of a minter. The minter is a separate jig that regulates the number of mintee jigs produced. In the example on the right, the GameItems jig class is the minter and is owned by the game. The Weapon jig object is the mintee and is owned by a player. The Weapon class enforces that it can only be created by GameItems, and the GameItems class limits the supply of Weapon.

In Weapon.init, we check that the weapon is being created by a GameItems instance using the caller special property. caller is the jig that called the current method being executed. If a player tried to create a Weapon instance independent of GameItems, Run would throw an error because the caller property would be null.

In GameItems, we check that there are no more than 10 items created by the game in createWeapon(owner). This limits the supply. However, we also have to check that GameItems is only able to be created by the game company. This is to prevent users from creating their own GameItems classes to mint their own weapons.

Limiting Interactivity

Create a token that can only interact with itself

class MoneyBucks extends Token { }

MoneyBucks.interactive = false

run.deploy(MoneyBucks)

By default, any jig may interact with any other jig in a transaction. Jigs can be swapped, stored on each other, created from each other, and more. However, the downside here is that when jigs interact with other jigs, all of the code for those other jigs needs to be trusted by anyone who wishes to load those jigs thereafter. Run maintains a global trust list, but apps and token issuers may want to avoid the risk that there jigs may be un-loadable by restricting their code and jig instances so that they can only interact with certain other jigs.

To limit interactivity like this, you can set the interactive property on any class or function to false. When code is non-interactive, it can only interact with itself, its instances, and references is has to other code. Run enforces this.

Dynamic Whitelists

A dynamic whitelist to support new tokens over time

class SupportedClasses {
  init() { this.classes = new Set() }
  add(T) { this.classes.add(T) }
}

const whitelist = new SupportedClasses()

class Game {
  init() { this.items = new Set() }

  use(jig) {
    expect(Game.whitelist.classes.has(jig.constructor)).toBe(true)

    this.items.add(jig)
  }
}

Account.whitelist = whitelist 

An app may want to allow third-party developers to create new classes of jigs. Those jigs can freely interact with existing jigs once approved. For this, you can create a dynamic whitelist of approved classes that jigs can check. See the example to the right ๐Ÿ ฎ

Atomic Swaps

Atomically swapping two items with different owners

Machine 1

const mine = await run.load(myLocation)
const theirs = await run.load(theirLocation)

const me = mine.owner
const them = theirs.owner

const tx = new Run.Transaction()
tx.update(() => mine.send(them))
tx.update(() => theirs.send(me))
const rawtx = await tx.export()

Machine 2

const tx = await run.import(rawtx)

if (tx.outputs.length) {
  // TODO: Inspect the transaction to be sure it atomically swaps
}

await tx.publish

Run supports atomically updating jigs with different owners using the Transaction API. One application for this is atomic swaps, where two jigs owned by different users are exchanged in a single transaction.

The general process for an atomic swap is for one user to start a Run transaction by calling new Run.Transaction(). Then, this user performs all updates inside an update() call on the transaction, including calling methods on jigs they don't own. Run allows this user to build the transaction even if they won't be able to sign for every jig. Finally, this first user calls tx.export() to export the transaction, which will pay and sign for it in the process. The transaction is now built and must now be handed to other parties for them to sign. The other party then calls run.import() to load the transaction they received. They may then want to check the transaction by inspecting tx.outputs and Run.util.metadata. If they approve, then they may call tx.publish() to sign and publish. If more signatures are needed, they can export the transaction instead of publishing.

The Transaction API may also be used to simulate state channels and propose changes to other jigs. See the Transaction API for more information.

Using Run on a Server

You may want to use Run on your server. For example you can:

The architecture you choose will depend on your application. You might create a REST service using express that allows users to make requests. Or you might index all jigs using a Planaria crawler. Or you might deploy a serverless function that responds to events. Run supports all of these architectures.

No matter the approach, you'll likely use Node on your servers. Run requires at least version 10 of Node. You can check which version you are using with node --version. If you are using Google Functions, be sure to set { "engine": 10 } in your package.json, because it defaults to Node 8.

Here are a few more tips:

Increase Cache Limits

Increase the state cache size

run.cache.sizeMB = 1000

The Cache stores jig state and blockchain transactions. By default, it caches 10MB of data in memory. This can be increased by setting run.cache.sizeMB. Alternatively, you can choose to persist state on disk rather than in memory.

After increasing these limits, you may also need to increase Node's memory limits. If you launch your server by starting node manually, you can increase its memory via: node --max-old-space-size=8192.

Persist the Cache

Save state into a Firestore collection

class Firestore {
  async get(key) {
    const entry = await db.collection('state').doc(key).get()

    if (entry.exists) return entry.data()
  }

  async set(key, value) {
    return db.collection('state').doc(key).set(value)
  }
}

const run = new Run({ cache: new Firestore() })

It is a good idea to persist your cache in a database so your backend never needs to load the same jig twice. It is even more important when using multiple servers because you can share this database. Simply implement the Cache API.

Run will automatically call get on your Cache implementation when values are needed, and set when values are ready to be cached. Cached values will never change for a given key. Just implement these two methods to load and save the cache into your preferred key-value database: Redis, Firestore, DynamoDB, etc.

To the right is an example of persisting in Google Firestore. You may want to tweak it so that it uses an in-memory local cache first and then a fallback to Google Firestore if that misses.

Avoid Race Conditions

If you are seeing server errors about Missing inputs or txn-mempool-conflict, it is likely that you have encountered a race condition. Two pieces of code are likely attempting to update the same jig at the same time, and are attemping to spend the same Bitcoin outputs. The network can only accept one. To solve this, here are a few recommendations:

Load jigs once at the start: If you have jigs you are reused across many request handlers, then load those jigs once upon starting your server, rather than loading them each time they are used. This will let Run track the output as a single object, and successive updates on that object will be correctly enqueued.

Separate server, separate purses: Every Run instance has its own internal update queue, so it is unlikely that the purse will be double-spent on a single server. But on multiple servers, if a purse key is shared, then it is very likely that double-spend errors will occur. To avoid this, use different purse keys on different servers.

Sync after every update: Call await jig.sync() on jigs after you call a method to be sure that the updates were applied before continuing on.

Serialize risky updates: If a request handler makes updates to multiple jigs, and these updates can conflict if interleaved across multiple users, then consider serializing these updates. The most reliable approach is to bundle all jig updates together in a batch, so that the operation is all-or-nothing. An alternative is to put updates into a task queue like p-limit.

Add retry logic: If the above changes do not fix a race condition, consider adding retry logic.

Load Only Safe Code

Even with sandboxing, arbitrary code has the potential for infinite loops and infinite memory attacks that can take down your server. Avoid running code that you do not trust. You should only add transaction IDs to your trusted set that you know are safe. Also, it is generally not recommended to call run.inventory.sync() on servers, because that will load UTXOs you receive from others.

Debugging

Sometimes you will be faced with errors you don't understand. Don't fret! Here are some tips:

Writing Unit Tests

It is always a good idea to write unit tests for your jigs. We recommend using a framework like mocha or jest, and running your tests using the mockchain. The mockchain will be faster and will isolate your Jig logic from any network errors.

Getting Help

Please reach out to @niv in the BSV slack channels or DM @runonbitcoin on Twitter and Twetch. We are happy to help you debug and fix any issues you encounter. If you can, see if you can reproduce the issue in the browser's web console.

How It Works

Overview

A jig is a JavaScript object that Run syncs with the blockchain. All of its code and method calls are stored on-chain in op_return metadata. Run can reconstruct the state of jigs by loading the code and replaying every method call. Every jig is paired with a Bitcoin output and only the owner of that output can make changes to the jig. All updates taken together form a transaction chain that enables consensus through user verification. This design is similar to other Layer-2 UTXO-based token systems because miners don't verify the JavaScript code. If anyone publishes an incorrect update, it not only destroys the jig but also leaves an immutable record.

Transaction

Sample code for a transaction

class Dragon extends Jig { }

const dragon = new Dragon()

A transaction in Run is an atomic update to jigs or code.

        Inputs -> Computation -> Outputs

Inputs are the jigs and code which will be updated. Computation is stored as executable statements in an op_return. The Run Virtual Machine executes these statements and produces outputs which are the jigs and code updated or created fresh. There may also be payment inputs and outputs too that are not used by Run but part of the transaction. Here is the transaction format for the example to the right:

Inputs Outputs
1. payment 1. op_return: Run metadata
2. p2pkh: Dragon Class
3. p2pkh: dragon jig instance
4. change output for payment

Inspecting Run metadata

const rawtx = await run.blockchain.fetch(txid)
const metadata = Run.util.metadata(rawtx)
console.log(metadata)

The op_return data is made up of both push data and a JSON payload. It has the following structure:

[op_false] [op_return] 'run' <version> '<app-id>' '<json-payload>'

You can easily identify Run transactions and its outputs using the op_return metadata.

JSON Payload

Sample JSON payload

{
    "in": 0,
    "ref": [
        "native://Jig"
    ],
    "out": [
        "e494cd3d0c33615620c22f44cddf85f2bf613fd608dbfc53822664581205d198",
        "9a99596f417e8925cb25f2acf99abe28f014aaad47ce93c427ee3afd3bcc5084"
    ],
    "del": [],
    "cre": [
        "mhhHzeLjRTD4cjuygJFjqmCHCFpDKGrp75",
        "mhhHzeLjRTD4cjuygJFjqmCHCFpDKGrp75"
    ],
    "exec": [
        {
            "op": "DEPLOY",
            "data": ["class Dragon extends Jig { }", { "deps": { "Jig": { "$jig": 0 } } }]
        },
        {
            "op": "NEW",
            "data": [{ "$jig": 1 }, []]
        }
    ]
}

The JSON payload stores Run-specific metadata. To the right is a JSON payload for the example above. Its fields are:

Name Description
in Number of jig and code inputs
ref Array of references to jigs and code used by not spent
out State hashes of jigs and code in transaction outputs
del State hashes of jigs and code deleted
cre New owners of jigs and code created
exec Statements to execute on the jigs

The exec field in particular contains the statements for Run to execute. There are 4 opcodes supported by Run in 0.6:

Opcode Description Data Format
DEPLOY Upload new code [<src1>, <props1>, <src2>, <props2>, ...]
NEW Instantiate a jig [<jig class>, <args>]
CALL Call a method on a jig [<jig>, <method>, <args>]
UPGRADE Replace code with new code [<code>, <src>, <props>]

The state hashes used in the out and del arrays are calculated by taking the sha-256 of the JSON serialization of the jigs. This state cache format will also be documented soon. Finally, the Run VM provides certain native classes, like Jig and Code, that are built-in to its virtual machine. Eventually this virtual machine will be stored on-chain too ensuring total trustlessness.

The protocol will be documented more in a coming spec. Reach out for any questions!

Serialization

Pass a sidekick object in by value

class Person {
    constructor(name, creationDate, profilePicture) {
        this.name = name
        this.creationDate = creationDate
        this.profilePicture = profilePicture
    }

    valid() { ... }
}

class SocialGroup extends Jig {
    constructor() { this.members = [] }

    add(person) {
        expect(person).toBeInstanceOf(Person)
        expect(person.valid()).toBe(true)

        this.members.push(person)
    }
}

SocialGroup.deps = { Person, expect }

const group = new SocialGroup()

group.add(new Person('Li', new Date(), null))

Every jig, every argument to a method, and every static property of a class, must be serialized so that it can be restored later. Run uses a custom serialization format that is similar to JSON. The following data types are supported:

All JavaScript IEEE 754 numbers types are serializable, including Infinity, NaN, and -0. Properties may be safely set on Sets, Maps, and Arrays, including any of the above types. Circular references are OK.

Symbols are not however serializable and will not be supported. Other JavaScript objects that are not currently supported include Date, WeakMap, Promise, Error, Float32Array, and BigInt.

Sidekick objects are JavaScript objects whose constructor is a sidekick class. Whereas jigs have blockchain locations and owners, and Run enforces that their state progresses from their method calls, sidekick objects are much simpler. They are stored by-value in their current state. A lock instance is an example of a sidekick object. In effect, they are data blobs with methods from on-chain code.

Sandboxing

All Code is sandboxed so that it does not have access to your application code, including your private keys! This provides security and also determinism when replaying a jig's history because a jig should evaluate the same in every environment. Run uses Agoric SES, the gold standard in secure JavaScript evalution, combined with the vm library in node and hidden iframes in the browser, to perform this sandboxing. Despite this, it is recommended you avoid running untrusted code.

Safety Checks

Run also protects you from performing updates on your jigs that will not be valid by others. For example, Run will throw an error if you try to change a property on a jig directly rather than call a method, or if you attempt to set a function as a property on a jig. Only top-level method calls may change a jig's state which is also enforced by Run. In addition, every Run transaction is pre-verified before it is published on your local machine.

Ownership Rules

Jigs are special objects that have ownership rules which Run enforces. These include:

Likewise, without ownership, the above actions cannot be performed. Proving ownership requires spending a Bitcoin output, so when ownership changes on a jig, further updates to that jig must occur in a new transaction. There is also a fourth rule of ownership that is a right granted to all jigs:

Privacy

All jigs and jig code are stored on-chain and are open for everyone to read. This enables anyone to reconstruct the state of any jig. Privacy is achieved similar to Bitcoin UTXOs. For maximum privacy, public keys should not be re-used. In the future, we will introduce private jigs that are visible only to the current owners, but by their nature, they will not work as well with explorers, analytics and other services.

Backwards Compatibility

Starting in Run 0.6, which uses protocol 0x05, all jigs will be backwards-compatible. This means jigs you launch today will always be supported into the future by the Run library. This is our promise. To keep this promise, every jig is connected to its protocol version, so as Run is updated, older jigs can be kept operational with their original behavior. In addition, we run tests against on-chain jigs with every update to ensure that your jigs continue to work.

API Reference

Run

class Run { ... }

A Run instance coordinates activity between the application, its jigs and other creations, and the blockchain. You always create a Run instance when you use the Run library. This stores your settings, creates and signs transactions, loads jigs and code, connects to the blockchain network, securely sandboxes code, keeps an inventory of jigs, keeps a cache for fast loads, and more.

constructor(options)

Create a Run instance on Mocknet. The purse will be auto-funded.

const run = new Run({ network: 'mock' })

Create a run instance on Testnet

// Create a run instance on testnet
const run = new Run({
  network: 'test',
  owner: 'cTMHbJULREfbsUFsuJLMrNbJ7VrASgWeYfFF6EgJMy49ARnNed3d',
  purse: 'cQP1h2zumWrCr2zxciuNeho61QUGtQ4zBKWFauk7WEhFb8kvjRTh'
})

constructor(options: object): Run

Creates a new Run instance.

The constructor takes many possible options, but the three most important options are network, owner, and purse. The network indicates which Bitcoin network to connect to. The owner and the purse are generally both private keys. The owner stores the user's jigs. Each live jig has a UTXO associated with it assigned to the owner's address. The purse is a separate private key used to pay the mining fees for Run transactions. It is best if the owner and purse are separate accounts so that jigs and money are kept in different wallets.

Creating a new Run instance will automatically activate it. See run.activate() for more information.

Options

Property Type Description Default
api string One of 'run', 'mattercloud', or 'whatsonchain' 'run'
apiKey string Blockchain API key for MatterCloud or WhatsOnChain. undefined
app string Application id to distinguish Run transactions. See How It Works. Empty string
blockchain Blockchain Blockchain implementation for interacting with the Bitcoin network. If specified, then network, api, and apiKey are ignored. RunConnect
cache Cache Cache API implementation that Run will use instead of the default LocalCache or BrowserCache
client boolean Whether to only load jigs from the cache and not replay transactions false
logger Logger Logging object for internal run messages. If logger is null, then nothing will be logged. You may pass console as the logger to log everything. Logs warnings and errors to the console
network string Bitcoin network, either main, test, stn, or mock. main
networkTimeout number Timeout for network requests in milliseconds 10000
networkRetries number Number of times to retry network requests 2
owner string or Owner Private key, public key, address, or custom lock used to own jigs and sign transactions Randomly generated LocalOwner
purse string or Purse Private key or Purse API used to pay for transactions. On the mock network, the purse will be funded automatically. For other networks, the user must fund the purse. Randomly generated LocalPurse
timeout number Timeout for all Run actions in milliseconds 30000
trust Array<string> or '*' IDs of transactions whose code is known not to be malicious Empty array
wallet Owner and Purse A shorthand for a single object that implements both Owner and Purse. If specified, then owner and purse are ignored. undefined

activate()

activate()

Sets this instance to Run.instance, the active Run instance. All jig instantiations, updates, and class deployments will occur on the active run instance. The owner of this instance will sign transactions and its blockchain and purse are the ones which will be used. activate() will also assign bsv.Networks.defaultNetwork to be the active Run's network configuration.

deploy(code)

Upload a BigInteger class to the blockchain

class BigInteger {
  // ...
}

run.deploy(BigInteger)

await run.sync()

deploy(T: function): Code

Uploads code to the blockchain. Once deployment completes, the class or function will be assigned a location so that it may be downloaded later. It will also be assigned an origin and owner. deploy also sandboxes the class or function and returns it as Code with special Code methods. Code uploaded does not necessarily need to be a Jig class โ€” any class or function will work.

import(rawtx, options)

Import a Run transaction to update it

const tx = await run.import(rawtx)

tx.update(() => run.deploy(AnotherClass))

const updated = await tx.export()

import(rawtx: string, options: ?object): Promise<Transaction>

Imports a Bitcoin transaction into a Run.Transaction object that can be updated.

Options

Property Type Description Default
trust boolean Whether to automatically trust the transaction being imported false

load(location, options)

Load a particular jig

const ticketLocation = 'afc557ef2970af0b5fb8bc1a70a320af425c7a45ca5d40eac78475109563c5f8_o1'
const ticket = await run.load(ticketLocation)

Load a code creation

const classLocation = 'e4a9618d3a187448feeb95ff30f7ec6381a025316cdcae2cdf388ad3de7fab6f_o2'
const MyClass = await run.load(classLocation)
const object = new MyClass()

load(location: string, options: ?object): Promise<Creation>

Universal load method to load any creation including jigs objects, jig classes, sidekick code, berries, and more. You pass in an on-chain location. The location is generally a transaction id and output index pair. This string should usually be the jig's or class's last known location property. However, it is also possible to load jigs at any state in the past using other locations, although these will be read-only. Downloaded code will be safely sandboxed before being returned.

Options

Property Type Description Default
trust boolean Whether to automatically trust the transaction being imported false

sync()

Wait for all jigs to deploy and classes to upload before continuing

class MyClass { }
run.deploy(MyClass)

class MyObject extends Jig { }
const jig = new MyObject()

await run.sync()

console.log('jig origin', jig.origin)
console.log('class origin', MyClass.origin)

sync(): Promise<void>

Returns a promise that completes when all pending transactions are published. After this completes, all jigs and classes will be assigned locations. Note: This method will not update the inventory with newly received UTXOs. For that, call run.inventory.sync().

transaction(f)

Send two tokens and deploy a class all in the same transaction

run.transaction(() => {
  token1.send(address)
  token2.send(address)
  run.deploy(Trophy)
})

transaction(f: function): any

Shorthand for creating a new transaction, calling update with the function passed, and then calling publish.

trust(txid)

Trust a third-party transaction before loading its code

const codeTxId = codeLocation.slice(0, 64)
run.trust(codeTxId)

await run.load(codeLocation)

trust(txid: string)

Adds a transaction to the trusted set. By default, code that is not in the trusted set cannot be loaded.

app

app: string

An application ID to distinguish Run transactions. Application developers may wish to set this in Run's constructor in order to find their specific transactions in third-party tools and analytics, like trends.cash or WhatsOnChain. This string will be UTF-8 encoded and stored in the 5th chunk of the op_return script. For more details about the protocol, see How It Works.

blockchain

Download a transaction from the blockchain

const txid = 'afc557ef2970af0b5fb8bc1a70a320af425c7a45ca5d40eac78475109563c5f8'
const tx = await run.blockchain.fetch(txid)

blockchain: Blockchain

Blockchain API to access the bitcoin network. See the Blockchain API. You may change the blockchain anytime.

cache

cache: Cache

Cache API used to save and load existing state. You may change the cache anytime.

inventory

inventory: Inventory

Inventory that tracks creations owned by the current owner. This is reset whenever the Run owner changes.

logger

logger: Logger

Logger implementation that prints out Run information. You may change the logger anytime.

owner

owner: Owner

The owner object used to update jigs and approve transactions. See the Owner API. You may change the owner anytime.

purse

purse: Purse

The purse used to pay for transactions. You may change the purse anytime.

timeout

timeout: number

Timeout for all Run actions in milliseconds. This applies to load(), sync(), import() and the inventory's sync(). If set to Infinity, then there is no timeout.

static Jig

Create a custom jig that extends the Jig base class

class MyObject extends Run.Jig {
  // ...
}

static Jig: Jig

Jig class that is common for all Run instances. It is set as Jig at the global scope. You must extend from this base class to create a jig. See Jig.

static Code

static Code: Code

Code base class that all deployed classes or functions inherit from. See Code.

static Berry

static Berry: Berry

Berry base class that defines a method for loading third-party data and its structure. See Berry.

static Creation

static Creation: Creation

Creation base class that jigs, code, and berries all share. See Creation.

static Transaction

static Transaction: Transaction

Transaction class used to manually create, update, and inspect Run transactions. See Transaction.

static plugins

static plugins: object

Contains the plugins that ship with Run and may be used to configure Run. See Plugins

static extra

static extra: object

Contains the standard library of code jigs that ship with Run. See Extras.

static api

static api: object

An object that contains all plugin APIs that a user may implement. See APIs.

static util

static util: object

Various utility functions and classes. See Util

static configure(env, network)

Configure Run from the command line

Run.configure(process.env)

static configure(env: object, network: ?string)

Configures Run's defaults with the provided environment object. This is typically process.env. This provides an easy way for apps to setup Run from the shell. When Run is instantiated, it will use these settings as defaults.

The network parameter is optional. If specified, it overrides the value in the environment variables. Run will pick the PURSE and OWNER keys for that specific network.

Environment variables

Name Description Possible Values Default
APP App string provided to Run your app string ''
LOGGER Whether to log internal messages to the console true, false, debug false
NETWORK Network string mock, main, test, stn mock
PURSE Purse key used your string privkey undefined
PURSE_[network] Purse key used on a specific network your string privkey undefined
OWNER Owner key used your string privkey undefined
OWNER_[network] Owner key used on a specific network your string privkey undefined
API Blockchain API when using mainnet or testnet run, mattercloud, whatsonchain run
APIKEY API key for the blockchain API your string api key undefined
APIKEY_[api] API key used with a specific API your string api key undefined

static protocol

static version: number

The version number of the Run protocol. Jigs that you deploy will be tied to a particular protocol. Jigs and other creations deployed using protocol v5 and above and guaranteed to be supported in the future.

static version

static version: string

Semantic version of the Run library.

static instance

static instance: Run

The currently active Run instance. All jig instantiations, updates, and class deployments will occur on the active run instance. The owner of that instance will sign transactions and its blockchain and purse are the ones which will be used. You may change the active Run instance either by calling run.activate() or by creating a new Run instance.

Jig

class Jig extends Creation { ... }

Base class for objects and code that can be owned on the blockchain and updated by calling methods. Jigs have various internal rules that ensure they are able to be safely composed together and called from each other. All classes that extend from Jig are themselves jigs as well as Code. Jigs are also Creations and have location, origin, and nonce properties.

init()

Simple jig that takes two parameters in its constructor

class SimpleStore extends Jig {
  init(a, b) {
    this.a = a
    this.b = b
  }
}
const store = new SimpleStore(1, 2)
console.log(store.b) // prints 2

init()

The initialization method for jig objects. init() is to jigs as constructor() is to normal classes. Run will call init automatically when the class is instantiated, just like constructors, and init() cannot be called directly by users. You may pass parameters into init() and also call super.init() to call its parent initializer.

sync(options)

sync(options: ?object): Promise<Jig>

Synchronizes the jig object with the blockchain. If there are any pending transactions for this jig to publish, this method will wait for them to broadcast. If there are no pending transactions, then this method will update the jig and any internally-referenced jigs to their latest states on the blockchain.

This method supports two options: forward and inner. If you pass { forward: false }, then only pending transactions will be published and new updates will not be downloaded. This may be useful for performance reasons or if the Blockchain API does not support forward syncing, as is the case with WhatsOnChain. If you pass { inner: false }, then only the current jig will be updated and any internally-referenced creations will not be explicitly updated.

Options

Property Description Default
forward Whether to sync the jig forward to its latest on-chain state undefined
inner Whether to also sync any referenced creations undefined

destroy()

destroy(): Jig

Destroys the jig. The destroyed jig will acquire a final location that ends with _dN, where N is the index of the destroyed jig in its final transaction. This location is uniquely identifiable and the jig may still be referenced. However, it can no longer be updated. You may override the destroy() method to perform custom destroy behavior.

auth()

auth(): Jig

Proves ownership over the current jig by spending its output in a transaction. You may override the auth() method.

static load(location)

Load a jig

const msg = await Message.load('4e146ac161324ef0b388798462867c29ad681ef4624ea4e3f7c775561af3ddd0_o1')

static load(location: string): Promise<Berry>

Loads a jig object that is an instance of the particular jig class. This is an alternative to run.load() for when you know the type of jig to load. This method is accessible when loading berries unlike run.load().

Code

class Code extends Creation { ... }

Code is the base class for classes and functions deployed on-chain. Unlike Jig however, you do not need to extend from Code yourself. When you call run.deploy on a class or function, the returned sandboxed code is a Code instance that gets all the functions and properties below. This lets you to upgrade, send and destroy code just like object jigs! All code are also Creations and have location, origin, and nonce properties.

There are two kinds of Code: jig code and sidekick code. Jig code are classes that extend from Jig and behave just like jig objects, meaning their methods are protected and static function calls can update their state. Sidekick code are functions or classes that do not extend from Jig, and cannot be updated in that way over time. Sidekick code has no special rules unlike jigs and so is better suited for helper code. The Code base class is available via Run.Code. Both jig code and sidekick code however may be upgraded, destroyed, and signed.

sync(options)

sync(options: ?object): Promise<Code>

Synchronizes code with the blockchain. If there are any pending transactions to publish, this method will wait for them to broadcast. If there are no pending transactions, then this method will update the code and any internally-referenced creations to their latest states on the blockchain.

This method supports two options: forward and inner. If you pass { forward: false }, then only pending transactions will be published and new updates will not be downloaded. This may be useful for performance reasons or if the Blockchain API does not support forward syncing, as is the case with WhatsOnChain. If you pass { inner: false }, then only the current jig will be updated and any internally-referenced creations will not be explicitily updated.

Options

Property Description Default
forward Whether to sync the code forward to its latest on-chain state undefined
inner Whether to also sync any referenced creations undefined

destroy()

Destroy a token class during deploy to stop future mints

class GiftCard extends Token { }

run.transaction(() => {
  GiftCard.mint(1000)
  GiftCard.destroy()
})

destroy(): Code

Destroys the code so that it cannot be updated in the future. The destroyed code will acquire a final location that ends with _dN, where N is the index of the destroyed code in its final transaction. This location is uniquely identifiable and the code may still be referenced and instantiated. However, it may no longer be updated.

auth()

Enforce that only the owner of a jig class may create instances

class Coin extends Jig {
  init() {
    Coin.auth()
  }
}

auth(): Code

Proves ownership over the current code by spending its output in a transaction.

upgrade(T)

Upgrade a jig class to add a new method

const DragonCode = await run.load(classLocation)

class Dragon extends Jig {
  setName(name) { this.name = name }
  setAge(age) { this.age = age }
}

DragonCode.upgrade(Dragon)

await DragonCode.sync()

upgrade(T: function)

Replaces the code with new behavior. With great power comes great responsibility. This method can completely change the behavior not just of the class but of any instances. It is best to use this feature to make bug fixes but not major changes. Once you are confident in the security of the code, you can set the upgradable property to false to prevent further upgrades. Jig objects, as instances of jig code, will be automatically upgraded to the new behavior when they call sync() and their class reference gets upgraded.

deps

static deps: ?object

The dependencies of this code. These must be manually specified in order for Run to load them into the code sandbox. And properties on deps are added as globals within the sandbox.

interactive

interactive: boolean

Whether the code is allowed to openly interact with any other code. The default value is true. However, you may wish to set this value to false if you only want your class and its jigs to interact with itself and any of its code references. You can use this create a code family where only code, and jigs created from that code, in the group are allowed to interact together.

sealed

sealed: true | false | 'owner'

The sealed property defines the rules for how a class can be extended. If true, then no extensions are allowed. If false, then anyone may extend from the class. If 'owner', then the class's owner must authorize of any extensions to the class. The default value is 'owner' so that a class hierarchy can be trusted and the instanceof keyword is safe by default.

upgradable

upgradable: boolean

Whether the code is allowed to be upgraded. You may wish to set this value to false to prove that the behavior will not change. The default value is true.

Berry

Define a berry class

class TwetchPost extends Berry {
    init(text) {
        this.text = text
    }

    static async pluck(location, fetch) {
      const data = txo(await fetch(location))
      if (data.out[0].s2 === '19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut') { // B protocol
        return new TwetchPost(data.out[0].s3)
      }
    }
}

TwetchPost.deps = { txo }

class Berry extends Creation { ... }

Berry is a base class for defining how data from third-party protocols should be loaded. Each class that extends from Berry defines a type of data. Berry instances, or simply berries, are read-only objects that represent this parsed data. Once loaded, but they may be used within jigs. All berries are also Creations and have location, origin, and nonce properties.

init()

init()

The initialization method for berry instances. You may pass parameters into init() and use those parameters to setup the berry. The pluck() method must create a new instance of the berry which will call init().

static pluck(path, fetch)

pluck(path: string, fetch: function): Promise<Berry>

Plucks a berry from the blockchain. You will override this method to load a berry instance. The berry class should fetch the transaction using async fetch(). This will return a raw transaction in hex format. The Berry should parse the transaction, perhaps using Tx or txo, and then instantiate a new Berry of the appropriate type with the data parsed out. If additional berries are required to build this one, they may be plucked recursively using Berry.load().

static load(path)

Load a berry

const post = await TwetchPost.load('4e146ac161324ef0b388798462867c29ad681ef4624ea4e3f7c775561af3ddd0')

static load(path: string): Promise<Berry>

Loads a berry of a particular type. Run internally calls the berry class's pluck() method to load the berry. load() may be also called recursively inside of Berry.pluck() to load sub-berries. You cannot override load().

Creation

Check that objects are indeed creations

Token2 instanceof Creation
// true

new Dragon() instanceof Creation
// true

twetchPostBerry instanceof Creation
// true

class Creation { ... }

A JavaScript object that Run can be loaded from the blockchain. This includes jigs, code, and berries. The Creation class, available via Run.Creation is the effective base class for all creations. All creations have the location bindings location, origin, and nonce as well as UTXO bindings owner and satoshis. They can be passed into jig functions and referenced as properties. To load a creation, call run.load with its corresponding location.

origin

Two different tickets have different origins

class Ticket extends Jig { }
const ticket1 = new Ticket()
const ticket2 = new Ticket()
await run.inventory.sync()
// ticket1.origin !== ticket2.origin

origin: string

The unique blockchain location where the creation was initially deployed or loaded that distinguishes it from all other creations. The format for origin is generally TXID_oN where TXID is the bitcoin transaction ID where the creation was first deployed. o is shorthand for output and N is the zero-based index representing this creation. Deleted creations, instead of ending in _oN, end in _odN instead. Berries have additional query parameters in their origin. In any case, the origin is set automatically by Run after deploying or loading the creation and it becomes read-only thereafter. Jig methods may internally read the origin anytime after it is initially published.

location

The value of location changes with every update

class Weapon extends Jig {
  upgrade() {
    this.upgraded = true
  }
}

const weapon = new Weapon()
await weapon.sync()
// weapon.origin === weapon.location

weapon.upgrade()
await weapon.sync()
// weapon.origin !== weapon.location

location: string

The blockchain location that uniquely identifies the current state of the creation. This is in the same form as origin. However, unlike origin, location will update with every state change on the creation. When there is a pending change, reading location will throw an error. This is because the Bitcoin transaction ID is not yet known so its location is undetermined. To avoid this error, be sure to call sync before reading the location. Finally, note that location, like origin, is a read-only property that Run updates for you.

nonce

nonce: number

The number of transactions this creation was updated in. This may be used to determine if one creation state is newer than another. Like origin and location, this is a read-only property that Run updates for you.

owner

Transferring ownership of a code jig

class FrequentFlyerRewards extends Jig {
  static transfer(to) {
    this.owner = to
  }
}

run.deploy(FrequentFlyerRewards).transfer(address)

owner: string|Lock

The owner is either a Bitcoin address string, a public key in hex, or a custom Lock that creates an output script. A jig may change this property to change owners, but it is read-only from outside the creation.

satoshis

Backing a jig with Bitcoin

class Item extends Jig {
  init() {
    this.satoshis = 10000
  }
}

satoshis: number

This number represents the amount of Bitcoin in satoshis that are backing this creation. It may be increased or decreased but it may not be set below zero. Increasing this value will deposit Bitcoin into the output for the jig from the Run purse. Decreasing it will withdraw Bitcoin to the Run purse.

Transaction

class Store extends Jig {
  set(value) { this.value = value }
}

const store = new Store()

const tx = new Run.Transaction()

tx.update(() => {
  store.set('Hello, world')
})

await tx.publish()

class Transaction { ... }

The Transaction class gives you to finer control over the current Bitcoin transaction being built by Run. This class is available via Run.Transaction. It allows you to:

Normally, when you update a jig that you own, Run will begin a transaction for you automatically. However, you can take control of this process by creating a new Transaction and calling update() on the transaction to perform the update yourself. When you are finished, you can call publish() to broadcast the transaction to the blockchain or export() to save the transaction locally. Alternatively, you may cancel the updates by calling rollback. Other parties may load your exported transaction by calling run.import and then inspecting its contents using outputs and deletes arrays. If they approve of the updates to their jigs, they may call sign and then publish to broadcast the transaction.

update(callback)

Deploy multiple jig classes in a single transaction

const tx = new Run.Transaction()
tx.update(() => run.deploy(Picasso))
tx.update(() => run.deploy(Monet))
tx.update(() => run.deploy(VanGogh))
await tx.publish()

update(callback: function)

Adds additional updates to the transaction. This may include calling a jig method, deploying code, upgrading code, destroying jigs, and more. The callback function passed in will be called to record the new changes.

publish(options)

publish(options: ?object): Promise<void>

Finalizes the transaction and publishes it to the blockchain.

Options

Property Description Default
pay Whether to pay for the transaction true
sign Whether to sign the transaction true

export(options)

export(options: ?object): Promise<string>

Exports the in-progress transaction as a raw hex string. This transaction may be imported or broadcast afterward.

Options

Property Description Default
pay Whether to pay for the transaction true
sign Whether to sign the transaction true

pay()

pay(): Promise<void>

Pays for the current transaction. This is performed automatically when publish() or export() is called, but you may wish to call it manually if you are paying with multiple purses or co-signing the transaction with others. If any additional updates are made to the transaction, including calling jig methods or deploying new code, then the transaction must be paid for again.

sign()

sign(): Promise<void>

Signs all inputs involved in the current transaction using the active Run owner. The transaction should only be signed after all updates are added and the transaction has been paid for via pay(). After all required signatures are added, then the transaction may be broadcasted, and if the transaction requires signatures from multiple different owners, then it must be exported and imported by other parties to be signed. If any additional updates are made to the transaction, including calling jig methods or deploying new code, then the signatures are reset and the transaction must be paid for again and re-signed by all parties.

rollback()

class Store extends Jig {
  set(value) { this.value = value }
}

const store = new Store()

const tx = new Run.Transaction()

tx.update(() => {
  store.set('Hello, world')
})

tx.rollback()

// store.value is undefined again

rollback()

Abandons the current transaction and reverts all changes to jigs and code. Any classes to be deployed will have their origins and locations cleared, and any jigs to be updated will revert to their prior state. Any jigs that are deployed for their first time will be reverted to an invalid state where they may no longer be used.

outputs

outputs: Array<Creation>

Jigs or code outputted from the transaction in their end state.

deletes

deletes: Array<Creation>

Jigs or code deleted within the transaction in their end state.

Plugins

This description describes various classes that ship with the Run library that may be used to manually configure Run. Each plugin is available under Run.plugins.

RunConnect

Connect to Run Connect Blockchain API

const blockchain = new Run.plugins.RunConnect()

const run = new Run({ blockchain })

class RunConnect implements Blockchain { ... }

A Blockchain implementation that connects to the Run Blockchain Server.

RunConnect may be accessed via Run.plugins.RunConnect.

constructor(options)

constructor(options: object): RunConnect

Creates a RunConnect instance with the provided configuration.

Options
Property Type Description Default
network string Either main or test main

BrowserCache

Create a BrowserCache with a higher in-memory cache size

const { BrowserCache } = Run.plugins

const cache = new BrowserCache({ maxMemorySizeMB: 100 })

const run = new Run({ cache })

class BrowserCache implements Cache { ... }

A multi-level Cache implementation designed for web browsers that persists values across sessions.

BrowserCache is the default implementation of the Cache API setup by Run when using browsers. BrowserCache is a wrapper around the LocalCache and an IndexedDB store. It uses the LocalCache to quickly store and access values from local memory and the IndexedDb store to persist values across browser sessions. When a value is written, it is written both to the LocalCache and to the IndexedDB store.

BrowserCache may be accessed via Run.plugins.BrowserCache.

constructor(options)

constructor(options: ?object)

Creates the browser cache. The options object may be used to configure the cache.

Options
Property Type Description Default
maxMemorySizeMB number Max size in megabytes (MB) of the cached data stored in memory 10 (10MB)
dbName string Database name 'run-browser-cache'
dbVersion number Database version 1
dbStore string Object storage name 'run-objects'

maxMemorySizeMB

maxMemorySizeMB: number

Size in megabytes (MB) of the in-memory cache. This value may be changed at runtime.

Inventory

Tracks jigs assigned to the current owner. An Inventory is automatically created with a new Run instance and assigned to Run.inventory.

jigs

Load all jigs that are tickets

// Update the inventory's jigs
await run.inventory.sync()

// Find the ticket
const ticketClassOrigin = 'e4a9618d3a187448feeb95ff30f7ec6381a025316cdcae2cdf388ad3de7fab6f_o1'
const ticket = run.inventory.jigs.find(jig.constructor.origin === ticketClassOrigin)

jigs: Array<Jig>

Returns an array of all jig objects owned by the Run owner. This array is cached. To update it, call run.inventory.sync().

code

Load the user's classes

// Update the inventory's code
await run.inventory.sync()

// Find all classes we known that extend from a known origin
const itemBaseClassOrigin = 'e4a9618d3a187448feeb95ff30f7ec6381a025316cdcae2cdf388ad3de7fab6f_o2'
const ItemBase = await run.load(itemBaseClassOrigin)
const itemClasses = run.inventory.code.find(T => Object.getPrototypeOf(T) === ItemBase)

code: Array<function>

An array of all jig classes and functions owned by the Run owner. This array is cached. To update it, call run.inventory.sync().

It is best to identify code using origins as seen on the right. Identifying classes by their names is not secure.

sync()

sync(): Promise<void>

Updates the local jig objects and code with the latest UTXO set.

LocalCache

class LocalCache implements Cache { ... }

An in-memory LRU Cache implementation with a maximum size. LocalCache is the default implementation of the Cache API when using Node. The Run class will create one automatically if no cache is provided.

LocalCache may be accessed via Run.plugins.LocalCache.

constructor(options)

constructor(options: ?object)

Creates the local cache. The options object may be used to configure the cache.

Options
Property Type Description Default
maxSizeMB number Max size in megabytes (MB) of the cached data stored in memory 10 (10MB)

maxSizeMB

maxSizeMB: number

The maximum amount of data stored in the state cache in megabytes.

If this value is less than the current size, the cache will be shrunk to fit.

LocalOwner

class LocalOwner implements Owner { ... }

The default Owner implementation that uses a local private key to sign transactions. It is able to sign both standard locks (including addresses and public key strings) as well as group locks. Jigs created will all be assigned to the same address.

LocalOwner may be accessed via Run.plugins.LocalOwner.

constructor(options)

constructor(options: object): LocalOwner

Creates a LocalOwner with the provided configuration.

Options
Property Type Description Default
privkey string or bsv.PrivateKey Private key used to own jigs and other resources Randomly generated
blockchain Blockchain Blockchain used to query UTXOs. If none is specified, then the inventory will not be synced. None

privkey

privkey: string

Hex private key string used to sign jig updates. This is read-only.

address

address: string

Address used to assign to new jigs. This is read-only.

LocalPurse

class LocalPurse implements Purse { ... }

The Purse implementation that Run uses by default to pay for transactions using a local wallet. This is an implementation of the Purse API.

LocalPurse may be accessed via Run.plugins.LocalPurse.

constructor(options)

constructor(options: object): LocalPurse

Creates a LocalPurse with the provided configuration.

Options
Property Type Description Default
privkey string or bsv.PrivateKey Private key used to own jigs and other resources Required
blockchain Blockchain Blockchain used to query UTXOs Required
splits number Number of UTXO splits to reduce mempool chain issues 1
feePerKb number Transaction fee in satoshis per kilobyte 500

privkey

privkey: string

Private key used to sign the transaction.

address

address: string

Address used to find UTXOs and receive payments.

splits

Change the number of purse UTXO splits

run.purse.splits = 100

splits: number

The minimum number of UTXOs that the purse must have. If the number of UTXOs is less than this value, then Run will automatically split your UTXOs the next time a Run transaction is generated. The default value for splits is 1.

Increasing this value may avoid the too-long-mempool-chain error. For more information, see Debugging Tips.

balance()

Querying the current balance

console.log('Satoshis', await run.purse.balance())

balance(): Promise<number>

Returns a promise that resolves to the current balance in satoshis of this purse. This is the sum of all satoshis in this purse's unspent outputs.

utxos()

Building a transaction using the purse

const utxos = await run.purse.utxos()
const tx = new bsv.Transaction().from(utxos).change(run.purse.address).sign(run.purse.privateKey)

utxos(): Promise<{txid: string, vout: number, script: bsv.Script, satoshis: number}>

Returns a promise that resolves to the current UTXOs of this purse.

MatterCloud

Connect to MatterCloud Blockchain API with the given API key

const blockchain = new Run.plugins.MatterCloud({ apiKey: '...' })

const run = new Run({ blockchain })

class MatterCloud implements Blockchain { ... }

A Blockchain implementation that connects to the MatterCloud API. Only mainnet is supported.

MatterCloud may be accessed via Run.plugins.MatterCloud.

constructor(options)

constructor(options: object): MatterCloud

Creates a MatterCloud instance with the provided configuration.

Options
Property Type Description Default
apiKey string API key to use. Currently, only MatterCloud API supports this option. undefined

Mockchain

Getting the mockchain from a run instance

const run = new Run({ network: 'mock' })

const mockchain = run.blockchain

Creating a mockchain manually and passing it into Run

const mockchain = new Run.plugins.Mockchain()

const run = new Run({ blockchain: mockchain })

class Mockchain implements Blockchain { ... }

The mockchain is a local simulation of a blockchain that stored entirely in memory. It implements the Blockchain API. It accepts and validates real Bitcoin transactions using testnet settings. The mockchain simulates many aspects of a real Bitcoin API, including the 25 chained mempool limit, but everything happens locally.

We recommend using the mockchain during development and unit testing. The mockchain lets you simulate Run without requiring network connectivity and without spending real bitcoins. Note that when the program closes however, all mockchain data is lost, so it is not a substitute for a real network.

The class is accessible via Run.plugins.Mockchain, or as an instance in run.blockchain after setting the network to mock.

constructor()

constructor()

Creates a new mockchain. There are no parameters.

fund(address, satoshis)

fund(address: string, satoshis: number)

Directly funds an address with an amount of satoshis without requiring the spending of any inputs. The Run class will automatically fund the purse, but you may fund additional addresses using this method.

block()

block()

Creates a new simulated block. This is useful when the 25 chained mempool limit is hit.

PayServer

Create Run and use the testnet pay server

const apiKey = '<your-api-key>'
const payServer = new Run.plugins.PayServer(apiKey)
const run = new Run({ network: 'test', purse: payServer })

class PayServer implements Purse { ... }

A Purse implementation that uses the Run Pay Server to pay for transactions.

PayServer may be accessed via Run.plugins.PayServer.

constructor(apiKey)

constructor(apiKey: string)

Creates the PayServer with a given API key. The API key may be generated via https://api.run.network/v1/test/pay/generate. Only testnet is supported today.

Viewer

Load the jigs owned by another user

const viewer = new Run.plugins.Viewer(customLock)

const run = new Run({ owner: viewer })

await run.inventory.sync()

// run.inventory.jigs will contain their jigs

class Viewer implements Owner

An Owner implementation for loading another user's jigs.

These resources will be read-only and the Viewer will not be able to sign for any updates.

Viewer may be accessed via Run.plugins.Viewer.

constructor(owner)

constructor(owner: string|Lock)

Creates a new viewer.

WhatsOnChain

Connect to WhatsOnChain API on testnet

const blockchain = new Run.plugins.WhatsOnChain({ network: 'test' })

const run = new Run({ blockchain })

class WhatsOnChain implements Blockchain { ... }

A Blockchain implementation that connects to the WhatsOnChain API.

WhatsOnChain may be accessed via Run.plugins.WhatsOnChain.

constructor(options)

constructor(options: object): WhatsOnChain

Creates a WhatsOnChain with the provided configuration.

Options
Property Type Description Default
network string Either main or test main

Extras

Run ships with several built-in code creations that are part of its standard library. These include the standard Token2 class for numerical tokens and several other helpers. Each code below is predeployed and available under Run.extra.

asm

Create a custom R-puzzle output script

asm(`OP_DUP OP_3 OP_SPLIT OP_NIP OP_1 OP_SPLIT OP_SWAP OP_SPLIT OP_DROP OP_HASH160 ${rhash} OP_EQUALVERIFY OP_OVER OP_CHECKSIGVERIFY OP_CHECKSIG`)

function asm(s: string): string

Converts a Bitcoin script string in ASM notation into its hex string format. This code is deterministic and safe to use within Jigs. It is often useful when creating custom owner locks.

It is available via Run.extra.asm and predeployed at the following locations:

Network Location
main 61e1265acb3d93f1bf24a593d70b2a6b1c650ec1df90ddece8d6954ae3cdd915_o1
test 1f0abf8d94477b1cb57629d861376616f6e1d7b78aba23a19da3e6169caf489e_o1

B

class B extends Berry { ... }

The B class loads file data stored on the blockchain into a JavaScript object. You may use B to load images, 3D models, CSS styles, and more, to attach to your jigs, to display in your apps, or for any other purpose. The B:// data format is well-established as a method for storing files on-chain, and services like Bitcoin Files allow you to upload and view them easily. Run uses B data to represent images for jigs. See Standard Metadata for more.

To load a B berry, call either B.load() or B.loadWithMetadata(). B will load the first output containing B data in the transaction.

It is available via Run.extra.B and predeployed at the following locations:

Network Location
main 6fe169894d313b44bd54154f88e1f78634c7f5a23863d1713342526b86a39b8b_o1
test 5435ae2760dc35f4329501c61c42e24f6a744861c22f8e0f04735637c20ce987_o1

load()

Load B:// data into an img element

B.load('2f3492ef5401d887a93ca09820dff952f355431cea306841a70d163e32b2acad').then(b => {
  imgElement.src = `data:${b.mediaType};base64, ${b.base64Data}`
})

load(txid: string): Promise<B>

Loads B:// data from the transaction with the ID passed.

loadWithMetadata()

Load B:// data with attribution metadata

const metadata = {
  title: 'T Rex skull icon',
  license: '[CC BY 3.0](http://creativecommons.org/licenses/by/3.0)',
  author: '[Delapouite](https://delapouite.com/)',
  source: '[game-icons.net](https://game-icons.net/1x1/delapouite/t-rex-skull.html)'
}

await B.loadWithMetadata('55e2c09672355e009e4727c2365fb61d12c69add91215ee3e9f50aa76c808536', metadata)

loadWithMetadata(txid: string, metadata: ?object): Promise<B>

Loads B:// data and then attaches the provided metadata to the berry.

This is useful for adding attribution information to data that lacks it. See the Standard Metadata for information on metadata fields.

base64Data

base64Data: string

The data stored using B:// represented in base 64.

filename

filename: string

The filename as declared by the B:// protocol data.

mediaType

mediaType: string

The W3 registered media type. Examples include image/svg+xml and image/png.

expect

class Post extends Jig {
  init(message) {
    expect(message).toBeString()

    this.message = message
  }
}

Post.deps = { expect: Run.extra.expect }

function expect(subject) { ... }

The expect function is arbitrary code to help you check with parameters in Jigs. It is similar to Jest or Chai assertions. expect takes a single argument, subject, and then lets you execute one of its assertion methods on ithe subject. If the assertion passes, then nothing happens, but if it fails, an Error is thrown.

Each method takes an optional last parameter message that is the error message to throw. If none is specified, then a default error message will be created.

It is available via Run.extra.expect and predeployed at the following locations:

Network Location
main 71fba386341b932380ec5bfedc3a40bce43d4974decdc94c419a94a8ce5dfc23_o1
test f97d4ac2a3d6f5ed09fad4a4f341619dc5a3773d9844ff95c99c5d4f8388de2f_o1

not

expect(name).not.toBeNumber()

not

The not property reverses the condition of any methods that follow.

toBe()

expect(power > 9999).toBe(true)

toBe(value: any, message: ?string)

Checks that the subject is equal to the value, using the javascript === operator. This does not, however, deeply compare values of an object, so for deep object comparison we recommend toEqual.

toEqual()

expect(names).toBe(['Stevie', 'Wonder'])

toEqual(value: any, message: ?string)

Deeply compares the subject to a value, which involves recursively traversing through every sub-property of objects to compare whether they are the same primitive values.

toBeInstanceOf()

expect(dragon).toBeInstanceOf(Dragon)

toBeInstanceOf(Class: Class, message: ?string)

Checks that an object is an instance of a class or one of its parents.

toBeDefined()

expect(message).toBeDefined()

toBeDefined(message: ?string)

Checks that a value is not undefined.

toBeNull()

expect(hat).not.toBeNull()

toBeNull(message: ?string)

Checks that a value is null. Often this is paired with not to check that a value is not null, as seen on the right.

toBeNumber()

expect(health).toBeNumber()

toBeNumber(message: ?string)

Checks that a value is a numerical type. Numbers including all integers, floating point numbers, NaN, and Infinity.

toBeInteger()

expect(amount).toBeInteger()

toBeInteger(message: ?string)

Checks that a value is an integer number and does not have a decimal point.

toBeLessThan()

expect(damage).toBeLessThan(50)

toBeLessThan(value: number, message: ?string)

Checks that a value is a number less than a particular number.

toBeLessThanOrEqualTo()

toBeLessThanOrEqualTo(value: number, message: ?string)

Checks that a value is a number less than or equal to a particular number.

toBeGreaterThan()

expect(name.length).toBeGreaterThan(3)

toBeGreaterThan(value: number, message: ?string)

Checks that a value is a number greater than a particular number.

toBeGreaterThanOrEqualTo()

toBeGreaterThanOrEqualTo(value: number, message: ?string)

Checks that a value is a number greater than or equal to a particular number.

toBeBoolean()

expect(weaponsEnabled).toBeBoolean()

toBeBoolean(message: ?string)

Checks that a value is either true or false.

toBeString()

expect(name).toBeString()

toBeString(message: ?string)

Checks that a value is a string. It may still be the empty string however.

toBeObject()

expect(properties).toBeObject()

toBeObject(message: ?string)

Checks that a value is a non-null object or an Array.

toBeArray()

expect(tokens).toBeArray()

toBeArray(message: ?string)

Checks that a value is an array. It may be empty.

toBeSet()

expect(whitelist).toBeSet()

toBeSet(message: ?string)

Checks that a value is a Set instance. It may be empty.

toBeMap()

expect(users).toBeMap()

toBeMap(message: ?string)

Checks that a value is Map instance. It may be empty.

toBeUint8Array()

expect(buffer).toBeUint8Array()

toBeUint8Array(message: ?string)

Checks that a value is a Uint8Array instance. It may be empty.

toBeClass()

expect(CustomLock).toBeClass()

toBeClass(message: ?string)

Checks that a type is a class.

toBeFunction()

expect(calculateDamage).toBeFunction()

toBeFunction(message: ?string)

Checks that a type is a function. Classes are not considered functions for this assert.

toBeJigClass()

expect(Dragon).toBeJigClass()

toBeJigClass(message: ?string)

Checks that a type is a class that extends from Jig.

toExtendFrom()

expect(MyToken).toExtendFrom(Token2)

toExtendFrom(T: function)

Checks that a class extends from another class.

Group

Create and then sign a 2-3 multi-sig

// Create a token owned by a 2-3 multi-sig
token.send(new Group([pubkey1, pubkey2, pubkey3], 2))

// Begin a transaction to spend it and sign with key #1
const run = new Run({ owner: privkey1 })
const tx = new Run.Transaction()
tx.update(() => token.send(address, 100))
await tx.pay()
await tx.sign()

// Co-sign with key #2
run.owner = privkey2
await tx.sign()
await tx.publish()

class Group implements Lock { ... }

A group lock is a m-of-n multi-sig output used to have more than one user own a resource. Combined with the LocalOwner and TransactionAPI, Run is able to sign group locks using its default LocalOwner in any order. See the example to the right.

A Group lock is as secure as bitcoins themselves. If some parties can update a jig, they can also destroy the jig.

It is available via Run.extra.Group and predeployed at the following locations:

Network Location
main 780ab8919cb89323707338070323c24ce42cdec2f57d749bd7aceef6635e7a4d_o1
test 63e0e1268d8ab021d1c578afb8eaa0828ccbba431ffffd9309d04b78ebeb6e56_o1

constructor(pubkeys, required)

constructor(pubkeys: Array<string>, required: number): Group

Creates a m-of-n Group for the set of public keys.

The maximum length of pubkeys is 16. If required is not specified, then it is the number of pubkeys.

add(pubkey)

add(pubkey: string)

Adds a pubkey to the pubkey list if it does not already exist.

pubkeys: Array

Array of public keys that are partial owners. There can be no more than 16 and they must be hex strings.

required: number

Number of signatures required to unlock the output. This is the m in m-of-n multi-sig.

Hex

It is available via Run.extra.Hex and predeployed at the following locations:

Network Location
main 727e7b423b7ee40c0b5be87fba7fa5673ea2d20a74259040a7295d9c32a90011_o1
test 1f0abf8d94477b1cb57629d861376616f6e1d7b78aba23a19da3e6169caf489e_o2

static stringToBytes

static bytesToString(b: Array<number>): string

Converts a array of bytes to a hex string. This method will throw if any entries in the array are not bytes.

static stringToBytes(s: string): Array<number>

Converts a hex string into an array of bytes. This method will throw if the string is not a a valid hex string.

Token

class Token extends Jig { ... }

Token is a standard base class for fungible tokens similar to ERC-20 or SLP. It may be used for shares, loyalty points, gift cards, and more. Each Token instance defines a numerical amount held by its owner. You extend from Token to define a new kind of token. As the owner of this new token class, only you can mint new token instances. The token instances you mint though may be sent, owned, traded, and combined together with others. Like bitcoins, they are permissionless and only require their owner's approval to use.

Token supports integer amounts like ERC-20 in its amount field. In practice, you will often wish to display the token's amount with a decimal, like 1.50. The static decimals property may be used to declare how many digits the amount is to be shifted when the amount is displayed. In the previous example, amount would be 150 and decimals would be 2.

It is available via Run.extra.Token and predeployed at the following locations:

Network Location
main 72a61eb990ffdb6b38e5f955e194fed5ff6b014f75ac6823539ce5613aea0be8_o1
test 7d14c868fe39439edffe6982b669e7b4d3eb2729eee7c262ec2494ee3e310e99_o1

static mint(amount, to)

Minting a new token

class MyCustomToken extends Token { }

const token = MyCustomToken.mint(100)

mint(amount: number, to: ?(string|Lock)): Token

mint() issues a new token with a specified amount. Only the owner of the extended token class is able to call this method. The newly minted token will be assigned to the to address if specified, or to to the token class's owner if not.

send(to, amount)

const token = new MyCustomToken(100)

const sent = token.send(pubkey, 20)

console.log(sent.amount) // 20
console.log(token.amount) // 80

send(to: string|Lock, amount: ?number): Token

Sends amounts from this token to another user, creating a new Token in the process. The returned token will have the amount specified and the current token will have its amount decreased accordingly. When its amount becomes zero, the token is automatically destroyed. If amount is not specified, when the entire token amount is sent.

combine(...tokens)

Combining three tokens newly minted

const token1 = new MyCustomToken(10)
const token2 = new MyCustomToken(20)
const token3 = new MyCustomToken(30)

const combined = token1.combine(token2, token3)

console.log(combine.amount) // 60

Combining and sending tokens in a single transaction

const tokens = run.inventory.jigs.filter(jig => jig instanceof MyCustomToken)

run.transaction(() => {
  const combined = tokens[0].combine(...tokens.slice(1))

  combined.send(pubkey, amount)
})

combine(...tokens: Token): Token

combine will merge the multiple tokens into a single token that has the combined amount. The tokens to combine must all be the same kind and the returned token object will be the same token that was called.

Tokens are like Bitcoin outputs. Every time you send some tokens, you split off an amount into a new output. Just like Bitcoins, you may later want to merge them back together. It is often useful to do this right before sending your tokens so that you can send their full amount. The example on the right shows how to combine and send tokens in a single transaction.

amount

amount: number

The integer value held within this token. The meaning of amount depends on the token.

static decimals

class USDToken extends Token { }

USDToken.decimals = 2

const dollars = new USDToken(100)

let displayAmount = dollars.amount
while (let i = 0; i < USDToken.decimals. i++) {
  displayAmount /= 10
}
console.log(displayAmount) // 1.00

static decimals: number

The number of decimal places to shift the amount when displaying it. Token uses integers by default for precision, just like ERC-20, but often times you will want to specify a decimals value to indicate a unit for display to the user. For example, a US Dollar coin might set decimals to 2 so that the base amount is cents but the display value is dollars.

Tx

Load an OP_RETURN into a berry

class OpReturn extends Berry {
  async static pluck(txid, fetch) {
    const rawtx = await fetch(txid)
    const tx = new Tx(rawtx)
    const script = tx.outputs[0].script
    if (!script.startsWith('6a')) throw new Error('No an OP_RETURN')
    return new OpReturn(tx.outputs[0].script)
  }

  init(script) { this.script = script }
}

OpReturn.deps = { Tx: Run.extra.Tx }

class Tx { ... }

Parses a raw hex transactions into an object that can be inspected. This is often useful when creating berries from transactions.

It is available via Run.extra.Tx and predeployed at the following locations:

Network Location
main 312985bd960ae4c59856b3089b04017ede66506ea181333eec7c9bb88b11c490_o2
test 33e78fa7c43b6d7a60c271d783295fa180b7e9fce07d41ff1b52686936b3e6ae_o2

constructor(rawtx)

constructor(rawtx: string): Tx

Parses the raw transaction into a Tx object. The constructor will throw an error if rawtx is not a valid Bitcoin transaction.

inputs

inputs: Array<{prevTxId: string, outputIndex: number, script: string, sequenceNumber: number}>

Transaction inputs array.

outputs

outputs: Array<{satoshis: number, script: string}

Transaction outputs array.

version

version: number

The version number of the transaction.

nLockTime

nLockTime: number

The lock time of the transaction.

txo

Parse a Twetch transaction into a berry

const { txo } = Run.extra

class TwetchPost extends Berry {
  init (text) {
    this.text = text
  }

  static async pluck (txid, fetch) {
    const data = txo(await fetch(txid))
    if (data.out[0].s2 === '19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut') {
      return new TwetchPost(data.out[0].s3)
    }
  }
}

TwetchPost.deps = { txo }

function txo(rawtx: string): object

Parses a raw hex transaction into a TXO data structure. This is often an easy way to interpret a transaction for a Berry.

The following fields are supported:

It is available via Run.extra.txo and predeployed at the following locations:

Network Location
main 312985bd960ae4c59856b3089b04017ede66506ea181333eec7c9bb88b11c490_o1
test 33e78fa7c43b6d7a60c271d783295fa180b7e9fce07d41ff1b52686936b3e6ae_o1

APIs

Check if a custom owner implementation will be accepted by Run

class MyOwner { /* implementation */ }

console.log(new MyOwner() instanceof Run.api.Owner)

This section describes APIs that may be implemented by the developer to customize Run. Each API is available under Run.api and some commonly-used implementations of these APIs are provided via the built-in Plugins. You may check that your implementation conforms to the interface by using instanceof on instances. You do not have to extend from the API classes for an implementation to be considered valid.

Blockchain

class Blockchain { ... }

The interface Run uses to communicate with the Bitcoin network. Run ships with several implementations: RunConnect, MatterCloud, WhatsOnChain, and the Mockchain. Developers may implement Blockchain to connect to the Bitcoin network in custom ways.

network

network: string

A friendly network string. This is usually one of main, test, stn, or mock, however it may be any string. If the network starts with 'main', the Run library will use mainnet settings wherever it matters. For all other networks, Run will use testnet settings.

broadcast(rawtx)

Create and broadcast a simple transaction

const rawtx = new bsv.Transaction()
  .from(utxo)
  .to(address, amount)
  .sign(privateKey)
  .toString('hex')

await run.blockchain.broadcast(rawtx)

broadcast(rawtx: string): Promise<string>

Submits a raw transaction in hex format to the network. A promise is returned that must resolve with the transaction ID if the transaction was accepted. If the network did not accept the transaction, then the promise should be rejected with the error. This method should resolve successfully for broadcasts of transactions which are already in the mempool.

fetch(txid)

Downloads a transaction from the network

const txid = 'afc557ef2970af0b5fb8bc1a70a320af425c7a45ca5d40eac78475109563c5f8'

const rawtx = await run.blockchain.fetch(txid)

const tx = new bsv.Transaction(rawtx)

fetch(txid: string): Promise<string>

Downloads a transaction from the network. A promise is returned that will resolve with the raw hex transaction or reject with the error if the transaction could not be retrieved.

utxos(script)

Downloads the current UTXOs for a given address

const address = 'mpBU73vq9ajhkXknP1sNGe6wjXH7QtwmWm'
const script = Script.fromAddress(address).toHex()
const utxos = await run.blockchain.utxos(script)
const tx = new bsv.Transaction().from(utxos).change(address)

utxos(script: string): Promise<Array<{txid: string, vout: number, script: string, satoshis: number}>>

Returns the unspent outputs for a given address. A promise is returned that will resolve with an array of UTXOs, which may be empty, or reject with an error. Each returned UTXO may be converted to a bsv.Transaction.UnspentOutput. The output script passed into utxos will be in hex format.

Usually, implementations will index UTXOs by the script's hash rather than the script itself. The utxos method takes a full script however to support partial compatibility for certain script patterns like P2PKH that often have dedicated query APIs. To calculate a script hash from a script using the bsv library, use the following: sha256(new Script(script).toBuffer()).reverse().toString('hex').

time(txid)

time(txid: string): Promise<number>

Returns the block time the transaction was confirmed, or the mempool acceptance time if not yet in a block, in milliseconds since the unix epoch.

spends(txid, vout)

spends(txid: string, vout: number): Promise<?string>

Returns the ID of the transaction that spends the given output, or null if the output is unspent.

If the Blockchain API does not support spends, it may return a Run.error.NotImplementedError, however, Run will only partially work in this case.

Cache

A cache implementation that stores data in the browser's local storage

class LocalStorageCache {
  async get(key) {
    const x = localStorage.getItem(key)
    if (x) return JSON.parse(x)
  }

  async set(key, value) {
    localStorage.setItem(key, JSON.stringify(value))
  }
}

class Cache { ... }

The interface Run uses to cache jig state, transactions, and other data needed for fast use. The Cache API also enables third-party state servers to exist and provide the intermediate state of jigs. All values stored are immutable and JSON-serializable. They should not be modified or created by hand.

The following keys will be set by Run:

Key Description
jig://<location> Serialized state for jigs of any kind including objects, code, or berries
tx://<txid> Raw transaction hex
time://<txid> Transaction time in milliseconds since the unix epoch
spend://<location> Transaction ID that spends the output
ban://<location> Jig location that is known to be unloadable

get(key)

get(key: string): Promise<?object>

Gets the value for a particular key. If this is an LRU cache, get() should also bump the key to the front.

Run will call this when loading jigs to see if it can short-circuit its loading. If a value exists and is valid state, Run will use it. This method is asyncronous to allow for third-party network calls.

set(key, value)

set(key: string, value: object): Promise<void>

Caches a value for a particular key.

Run will call this whenever a new transaction is published and also when a jig is loaded. This method is asyncronous to allow for third-party network calls.

Lock

Send a token to a P2PK output script

const { asm } = Run.extra

class PayToPublicKeyLock {
  constructor(pubkey) { this.pubkey = pubkey }
  script() { return asm(`${this.pubkey} OP_CHECKSIG`) }
  domain() { return 74 }
}

PayToPublicKeyLock.deps = { asm }

token.send(new PayToPublicKeyLock(pubkey))

class Lock { ... }

Locks are custom output scripts that may be assigned as owners to jigs. Every lock is an instance of a lock class that is deployed on-chain. The lock class's script() method defines how the output script is built from the lock instance's properties. Because the lock class is also deployed on-chain, jig code can use logic that relies on strongly typed owners. For example, perhaps a jig must be held by 3 people in a Group for it to be used.

script()

script(): string

Returns the output script for the current lock as a hex string.

Run will call this method when building your transactions. Run provides the asm utility function to more easily build these scripts. For security reasons, this function should always rebuild the script when it's invoked and never cache the results.

domain()

domain(): number

Returns the maximum size of this lock's unlocking script in bytes. Run uses this value to improve fee estimation for signatures exist by creating placeholders in unlocking scripts that are of size domain().

Calculating the domain may depend on various properties of the lock. As a rule of thumb, Bitcoin signatures are at most 74 bytes in script, and public keys when compressed are 34 bytes in script. It is best to err on the conservative side.

Logger

Write info, warn, and error messages to a file

class FileLogger {
  constructor(path) { this.stream = fs.createWriteStream(path, { flags: 'a' }) }

  info(...messages) { this.log('INFO', ...messages) }
  warn(...messages) { this.log('WARN', ...messages) }
  error(...messages) { this.log('ERROR', ...messages) }

  log(type, ...messages) { this.stream.write(`${type} ${messages.join(' ')} \n`) }
}

const run = new Run({ logger: new FileLogger('log.txt') })

class Logger { ... }

A custom logger API that may be used to intercept Run logs and send them to a more appropriate place. By default, Run will log errors and warnings to the console, but you may wish to log them to a custom file, or enable the info and debug logs too. It is not necessary to implement all methods below; whichever methods are implemented, Run will call.

info(...messages)

info(...messages)

Called when Run is performing a major action, such as loading a jig, or querying a REST API.

error(...messages)

error(...messages)

Called when Run detects an error. These are recommended for production logs.

warn(...messages)

warn(...messages)

Called when Run detects a possible error. These are recommended for production logs.

debug(...messages)

debug(...messages)

Called when Run creates debug messages. These will be more far verbose than you would typically expect in production logs.

Owner

An owner that runs on a REST server

class RemoteWallet {
  constructor(host, address) {
    this.host = host
    this.address = address
  }

  async nextOwner() { return this.address }

  async sign(tx, parents, locks) {
    const options = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: { tx, parents }
    }

    const rawtx = await fetch('/sign', options).then(r => r.json())
    return rawtx
  }
}

class Owner { ... }

An owner of jigs and other resources that is run.owner.

Run calls the Owner API to create new jigs and sign transactions for the user. It is different from the purse which is strictly for paying for transactions. This API supports both local and remote ownership via the async methods.

For a complete guide on implementing the Owner API for a third-party wallet, please reach out to @niv in the Atlantis slack.

nextOwner()

nextOwner(): Promise<string|Lock>

The owner address, pubkey, or lock, used to assign to new jigs.

sign(rawtx, parents, locks)

Sign all resources that are P2PK outputs

async sign(rawtx, parents, locks) {
  const tx = new bsv.Transaction(rawtx)

  for (let i = 0; i < tx.inputs.length; i++) {
    if (locks[i] instanceof PayToPublicKeyLock) {
      const sig = generateSignature(tx, i, parents[i], this.privateKey)

      tx.inputs[i].setScript(sig.toString('hex'))
    }
  }

  return tx.toString('hex')
}

sign(rawtx: string, parents: Array<?{satoshis: number, script: string}>, locks: Array<?Lock>): Promise<string>

Signs the transaction for the current jig owner.

The parents array is the same size as the transaction's inputs and contains a list of the parent outputs that are inputs in this transaction. This information may be needed to generate sighash values.

The third parameter, locks, are the jig owners for each jig input. This is a higher-level representation of the parent output scripts that allows the sign method to more easily determine which inputs to sign. If an input is not a jig, then its value in the array will be undefined. If the jig's owner is an address or public key, then the lock will be a CommonLock.

Purse

class Purse { ... }

An interface to pay for transactions that is the purse in Run. Run's default Purse implementation is LocalPurse

For a complete guide on implementing the Purse API for a third-party wallet, please reach out to @niv in the Atlantis slack.

pay(rawtx, parents)

Pay for a transaction using specific UTXOs

class PayWithUtxos {
    constructor(utxos, privateKey) {
        this.utxos = utxos
        this.privateKey = privateKey
    }

    async pay(rawtx) {
        const tx = new bsv.Transaction(rawtx)
        tx.from(this.utxos)
        tx.change(this.privateKey.toAddress())
        tx.sign(this.privateKey)
        return tx.toString('hex')
    }
}

const run = new Run({ purse: new PayWithUtxos(utxos, privateKey) })

pay(rawtx: string, parents: Array<{satoshis: number, script: string}>): Promise<string>

Pays for a transaction so that it is acceptable to miners.

The transaction is passed in raw hex format. Raw transaction do not have information about their parent outputs, so the second parameter is a parents array that is a 1-1 mapping with the inputs of the transaction. This method should return a paid transaction in raw hex format.

To pay for a transaction, the method should add inputs and outputs so that its fee is raised high enough to be accepted by miners, and then the purse should sign the inputs it adds. As of May 2020, an appropriate miner fee is 0.5 satoshis per byte. The transaction passed will include placeholder signatures for any jig inputs to help with fee estimation.

This method should not assume that all inputs and outputs are dust because there may be backed jigs that were updated or created. A third-party implementation can check that there are no backed jigs by looking for any non-dust inputs or outputs. Otherwise, the purse should be prepared to pay more than the miner fee to back jigs and receive change from unbacked jigs.

pay() is an asynchronous call that returns a promise. If an error is thrown, then the state of all jigs in the transaction will be reverted and the transaction will not be broadcasted. It is up to the purse to add retry logic if needed.

broadcast(rawtx)

broadcast(rawtx: string): Promise<>

A notification for purses when a transaction they paid for is being broadcast.

This method is optional for purses. It is designed for wallets to update their UTXOs. Wallets may also choose to broadcast the transaction themselves but if they do they should expect that the sometimes transaction might already be received by the network.

Util

This section describes various helper functionality made available via Run.util.

CommonLock

Send a token to a P2PKH address

token.send(new CommonLock(address))

class CommonLock implements Lock { ... }

The CommonLock generates a standard P2PKH output script for an address. It is created internally and automatically by Run whenever an address or public key is set as a jig owner. CommonLock instances are also passed into the locks array in the Owner API's sign method, and they may be created and used on their own as seen on the right. CommonLock may be accessed via Run.util.CommonLock.

constructor(address, testnet)

constructor(address: string, testnet: ?boolean): CommonLock

Creates a CommonLock for a specific address.

address

address: string

The address string for this lock.

testnet

testnet: ?boolean

Whether this address is intended for testnet (true), mainnet (false), or an unspecified network (undefined).

install(T)

Preinstall a class with presets to share it in a library

class Token extends Jig { ... }

Token.presets = {
  main: {
    location: 'b17a9af70ab0f46809f908b2e900e395ba40996000bf4f00e3b27a1e93280cf1_o1',
    origin: 'b17a9af70ab0f46809f908b2e900e395ba40996000bf4f00e3b27a1e93280cf1_o1',
    nonce: 1,
    owner: '1PytriYokKN3GpKw84L4vvrGBwUvTYzCpx',
    satoshis: 0
  }
}

module.exports = Run.util.install(Token)

install (T: function): Code

Installs a class or function as Code but does not deploy it nor require a Run instance to be created. If the resulting code is synced or referenced by another creation, it will automatically be deployed in the next transaction. This method is sometimes useful for providing sandboxed code to third-parties without requiring a Run instance to be created. For example, the built-in extras that ship with Run are pre-installed code.

metadata(rawtx)

Print out the Run metadata

Run.util.metadata(rawtx)

{
  "app": "",
  "version": 5,
  "in": 0,
  "ref": [
      "native://Jig"
  ],
  "out": [
      "e494cd3d0c33615620c22f44cddf85f2bf613fd608dbfc53822664581205d198",
      "9a99596f417e8925cb25f2acf99abe28f014aaad47ce93c427ee3afd3bcc5084"
  ],
  "del": [],
  "cre": [
      "mhhHzeLjRTD4cjuygJFjqmCHCFpDKGrp75",
      "mhhHzeLjRTD4cjuygJFjqmCHCFpDKGrp75"
  ],
  "exec": [
      {
        "op": "DEPLOY",
        "data": ["class A extends Jig { }", { "deps": { "Jig": { "$jig": 0 } } }]
      },
      {
        "op": "NEW",
        "data": [{ "$jig": 1 }, []]
      }
  ]
}

metadata(rawtx: string): object

Extracts all Layer-2 metadata from a Run transaction. The metadata function will throw an error if not passed a valid Run transaction in hex format. This method may be useful for debugging purposes and to determine whether a transaction is a Run transaction. The metadata returned is not intended to be executed outside Run. This metadata is stored in the first output using OP_RETURN. For more about this data structure, see How It Works. The metadata function may be accessed via Run.util.metadata.

unify(...creations)

Unify references so that two classes can be deployed together

const { unify } = Run.util

unify(Axe, Sword)

run.transaction(() => {
  run.deploy(Axe)
  run.deploy(Sword)
})

unify(...creations: Array<Creation>)

Synchronizes all creations so that they refer to the same objects in the same state. Run's protocol requires that creations are unified within a single transaction. This is usually performed automatically by Run before each method is executed. However, it may be useful to unify manually when using a Transaction when creations are used across multiple actions and cannot be automatically unified.

Tools

Explorer

Run ships with an explorer that you may use to view jigs and code. It is an excellent way to test that your objects are working. To give it a go, open getting_started.html and click Explorer. Then, paste any location or address into the search box.

Deploy

Generate keys and deploy code to testnet

> ./deploy --test lib/dragon.js 

No purse keys found. Generate new keys? (y/n) y

    Success!

    Your private keys are in this .env file. Please keep it safe.

    /home/yolanda/myproject/.env

Now send some funds to your testnet address

    mims9NN2AEdEQ8ULcTPdgU8TdE34oojky9

    โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–„
    โ–ˆ โ–„โ–„โ–„โ–„โ–„ โ–ˆโ–„โ–€ โ–€  โ–ˆโ–ˆโ–„โ–€โ–€โ–ˆ โ–ˆ โ–„โ–„โ–„โ–„โ–„ โ–ˆ
    โ–ˆ โ–ˆ   โ–ˆ โ–ˆ   โ–ˆโ–€  โ–€โ–€โ–ˆโ–„ โ–€โ–ˆ โ–ˆ   โ–ˆ โ–ˆ
    โ–ˆ โ–ˆโ–„โ–„โ–„โ–ˆ โ–ˆโ–„โ–ˆโ–€ โ–„โ–„ โ–„โ–ˆโ–ˆโ–€  โ–ˆ โ–ˆโ–„โ–„โ–„โ–ˆ โ–ˆ
    โ–ˆโ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–ˆโ–„โ–ˆ โ–ˆ โ–ˆโ–„โ–€โ–„โ–€โ–„โ–ˆ โ–ˆโ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–ˆ
    โ–ˆโ–„โ–„  โ–ˆโ–€โ–„โ–ˆ โ–ˆ โ–„ โ–€โ–„โ–ˆโ–ˆโ–„ โ–„ โ–„โ–€โ–ˆ โ–„โ–„โ–€ โ–ˆ
    โ–ˆ โ–„ โ–ˆโ–„โ–€โ–„โ–„โ–ˆ  โ–„โ–„ โ–„โ–ˆโ–„โ–ˆโ–€โ–€โ–€โ–€ โ–ˆโ–„โ–„โ–ˆโ–„โ–€โ–ˆ
    โ–ˆโ–„  โ–„โ–ˆโ–€โ–„ โ–ˆ โ–„โ–€   โ–„โ–€โ–€โ–€โ–ˆโ–ˆโ–ˆโ–ˆโ–„โ–ˆโ–€โ–„โ–€โ–€โ–ˆ
    โ–ˆโ–ˆโ–„โ–€ โ–ˆ โ–„โ–„โ–€ โ–€ โ–ˆโ–ˆโ–€โ–€ โ–ˆ โ–„โ–€ โ–€ โ–ˆโ–€โ–„โ–„โ–„โ–ˆ
    โ–ˆโ–„โ–„โ–ˆโ–€โ–ˆโ–ˆโ–„โ–€โ–„โ–„โ–€โ–ˆโ–„โ–„โ–€โ–€โ–ˆโ–„ โ–ˆ โ–„โ–€ โ–„โ–€โ–€โ–ˆโ–„โ–ˆ
    โ–ˆโ–„โ–ˆโ–€โ–€  โ–„โ–ˆ โ–„โ–ˆโ–€โ–ˆโ–ˆโ–ˆโ–„โ–€โ–€โ–„โ–ˆโ–„โ–ˆ    โ–€โ–ˆโ–€โ–ˆ
    โ–ˆโ–ˆโ–„โ–ˆโ–„โ–ˆโ–ˆโ–„โ–„โ–€โ–„โ–ˆโ–ˆโ–ˆโ–€โ–€โ–€โ–€โ–„ โ–„ โ–„โ–„โ–„   โ–€ โ–ˆ
    โ–ˆ โ–„โ–„โ–„โ–„โ–„ โ–ˆโ–ˆโ–€โ–€โ–€โ–€โ–€ โ–ˆโ–€โ–„โ–„โ–ˆ โ–ˆโ–„โ–ˆ โ–€โ–ˆโ–„โ–ˆโ–ˆ
    โ–ˆ โ–ˆ   โ–ˆ โ–ˆโ–€โ–ˆโ–„ โ–ˆโ–€โ–€โ–€โ–„ โ–ˆโ–€ โ–„โ–„  โ–„โ–„โ–„ โ–ˆ
    โ–ˆ โ–ˆโ–„โ–„โ–„โ–ˆ โ–ˆ   โ–ˆ โ–€โ–„โ–€โ–€โ–„โ–„โ–„โ–ˆ โ–ˆ โ–€ โ–„โ–€โ–„โ–ˆ
    โ–ˆโ–„โ–„โ–„โ–„โ–„โ–„โ–„โ–ˆโ–„โ–„โ–ˆโ–„โ–ˆโ–ˆโ–ˆโ–ˆโ–„โ–„โ–„โ–„โ–„โ–ˆโ–„โ–ˆโ–„โ–ˆโ–ˆโ–„โ–ˆโ–ˆ


    Waiting for funds to be sent...

    Success! Received 3000000 satoshis

Deploying code to test

Dragon.presets.test.location = 'd9f9b15d73298bb807c3b22f62cb1f0b096d2a1c13678a9d29836a6fb538f901_o6'
Dragon.presets.test.origin = 'd9f9b15d73298bb807c3b22f62cb1f0b096d2a1c13678a9d29836a6fb538f901_o6'
Dragon.presets.test.nonce = 1
Dragon.presets.test.owner = 'mjs1rSdsm6tGAKdm79cUQ4rYXY1kvho34v'
Dragon.presets.test.satoshis = 0

Success! Presets saved.

Deploying your jig classes ahead of time is a great best practice. It allows code to be re-used across jigs, and jigs to safely rely on types. For example, an event may only accept tickets that are instances of a SpecificTicket class. Pre-deployed classes make this possible, and save blockchain space and fees too! ๐ŸŒŽ

Deploying code yourself is possible, but to help, Run ships with a deploy tool. deploy is a command-line program to deploy your jig classes and update their presets. It can be found as dist/deploy and it is a self-contained app.

To use deploy, you'll specify:

deploy will find which classes to deploy based on your module's exports, deploy them to each blockchain, print out your new presets, and then update your source code to save the presets.

To setup your purse keys, it is recommended that you create a .env file to persist them. If you run deploy without a purse, it'll prompt you to create a new .env file and fund your new keys, as seen to the right. You can also pass a pre-existing purse on the command line. For example: deploy --purse=<my key> --main my-jigs.js.

For more details, see deploy --help.