Difference between revisions of "Advanced Usage"

From RunWiki
Jump to: navigation, search
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Standard Metadata ==
+
= Standard Metadata =
<p>Wallets, explorers, and exchanges will want to show your jigs. You can add special metadata to any jig to help these apps and services do so. Metadata is stored as an object called&nbsp;<code>metadata</code>&nbsp;on your jig, code, or berry, which contains various properties. The following common properties are called&nbsp;''standard metadata'':</p>
 
  
<p>'''Presentation'''''''''</p>
+
Wallets, explorers, and exchanges will want to show your jigs. You can add special metadata to any jig to help these apps and services do so. Metadata is stored as an object called metadata on your jig, code, or berry, which contains various properties. The following common properties are called standard metadata:
  
{| style="height: 161px;" &lt; thead&gt;
+
== Presentation ==
 +
 
 +
{| class="wikitable" style="border-collapse: collapse; width: 100%; height: 161px;"
 
|- style="height: 23px;"
 
|- style="height: 23px;"
! style="height: 23px; width: 96.9034px;" | Property
+
| style="width: 21.7029%; height: 23px;" | '''Property'''
! style="height: 23px; width: 35.2131px;" | Type
+
| style="width: 15.2898%; height: 23px;" |
! style="height: 23px; width: 453.849px;" | Description
+
'''Type'''
 +
| style="width: 63.0072%; height: 23px;" | '''Description'''
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>name</code>
+
| style="width: 21.7029%; height: 23px;" |
| style="height: 23px; width: 35.2131px;" | string
+
name
| style="height: 23px; width: 453.849px;" | String name to use in place of the class or function name
+
| style="width: 15.2898%; height: 23px;" |
 +
string
 +
| style="width: 63.0072%; height: 23px;" |
 +
String name to use in place of the class or function name
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>description</code>
+
| style="width: 21.7029%; height: 23px;" |
| style="height: 23px; width: 35.2131px;" | string
+
description
| style="height: 23px; width: 453.849px;" | Short sentence, less than 80 characters, that describes the jig for users
+
| style="width: 15.2898%; height: 23px;" |
 +
string
 +
| style="width: 63.0072%;" | Short sentence, less than 80 characters, that describes the jig for users
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>emoji</code>
+
| style="width: 21.7029%; height: 23px;" |
| style="height: 23px; width: 35.2131px;" | string
+
emoji
| style="height: 23px; width: 453.849px;" | Single UTF-8 emoji character to represent the jig
+
| style="width: 15.2898%; height: 23px;" |
 +
string
 +
| style="width: 63.0072%; height: 23px;" |
 +
Single UTF-8 emoji character to represent the jig
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>image</code>
+
| style="width: 21.7029%; height: 23px;" |
| style="height: 23px; width: 35.2131px;" | string
+
image
| style="height: 23px; width: 453.849px;" | Reference to an SVG or PNG image stored using B://
+
| style="width: 15.2898%; height: 23px;" |
 +
string
 +
| style="width: 63.0072%; height: 23px;" |
 +
Reference to an SVG or PNG image stored using B://
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>audio</code>
+
| style="width: 21.7029%; height: 23px;" |
| style="height: 23px; width: 35.2131px;" | string
+
audio
| style="height: 23px; width: 453.849px;" | Reference to an audio file stored using B://
+
| style="width: 15.2898%; height: 23px;" |
 +
string
 +
| style="width: 63.0072%; height: 23px;" |
 +
Reference to an audio file stored using B://
 
|- style="height: 23px;"
 
|- style="height: 23px;"
| style="height: 23px; width: 96.9034px;" | <code>glbModel</code>
+
| style="width: 21.7029%; height: 23px;" | glbModel
| style="height: 23px; width: 35.2131px;" | string
+
| style="width: 15.2898%; height: 23px;" |
| style="height: 23px; width: 453.849px;" | Reference to GLB file representing a 3d model stored using B://
+
string
 +
| style="width: 63.0072%; height: 23px;" | Reference to GLB file representing a 3d model stored using B://
 
|}
 
|}
  
 +
== Attribution and Licensing ==
 +
 +
 +
{| class="wikitable" style="border-collapse: collapse; width: 100%; height: 115px;"
 +
|- style="height: 23px;"
 +
| style="width: 21.1594%; height: 23px;" | '''Property'''
 +
| style="width: 16.5942%; height: 23px;" |
 +
'''Type'''
 +
| style="width: 62.2463%; height: 23px;" | '''Description'''
 +
|- style="height: 23px;"
 +
| style="width: 21.1594%; height: 23px;" |
 +
author
 +
| style="width: 16.5942%; height: 23px;" |
 +
string
 +
 +
markdown
 +
| style="width: 62.2463%; height: 23px;" |
 +
Name of the creator for the code or content
 +
|- style="height: 23px;"
 +
| style="width: 21.1594%; height: 23px;" |
 +
title
 +
| style="width: 16.5942%; height: 23px;" |
 +
string
 +
 +
markdown
 +
| style="width: 62.2463%; height: 23px;" |
 +
Title of the content
 +
|- style="height: 23px;"
 +
| style="width: 21.1594%; height: 23px;" |
 +
source
 +
| style="width: 16.5942%; height: 23px;" |
 +
string
  
<p>''''''</p>
+
markdown
 +
| style="width: 62.2463%; height: 23px;" |
 +
URL where the content was found
 +
|- style="height: 23px;"
 +
| style="width: 21.1594%; height: 23px;" | license
 +
| style="width: 16.5942%; height: 23px;" |
 +
string
  
{| class="wikitable" style="border-collapse: collapse; width: 100%;"
+
markdown
|-
+
| style="width: 62.2463%; height: 23px;" | License for the code or content
| style="width: 17.2464%;" | '''Property'''
 
| style="width: 13.4419%;" | '''Type'''
 
| style="width: 69.3116%;" | '''Description'''
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >name</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | String name to use in place of the class or function name
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >description</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | Short sentence, less than 80 characters, that describes the jig for users
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >emoji</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | Single UTF-8 emoji character to represent the jig
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >image</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | Reference to an SVG or PNG image stored using B://
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >audio</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | Reference to an audio file stored using B://
 
|-
 
| style="width: 17.2464%;" | <code class="mwt-code" >glbModel</code>
 
| style="width: 13.4419%;" | string
 
| style="width: 69.3116%;" | Reference to GLB file representing a 3d model stored using B://
 
 
|}
 
|}
 +
Media stored on-chain using the B:// protocol may be used as metadata for images, audio, or 3D models. To reference previously-uploaded data, use a string formatted either as "b://<txid>" or "b://<txid>_o<vout>". If vout is present, it must be zero-indexed. If it is not present, the first B output in the transaction is used. To upload B data, you may use the EasyB CLI tool.
  
<p>''''''</p>
+
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.
<p>'''Attribution and Licensing'''''''''</p>
 
  
{| &lt; thead&gt;
+
{| class="wikitable" style="border-collapse: collapse; width: 100%; height: 51px; background-color: rgb(248, 202, 198);"
|-  
+
|- style="height: 51px;"
! Property
+
| style="width: 100%; height: 51px;" | <span style="color: rgb(0, 0, 0);">'''Warning: '''For performance reasons, we recommended that B:// media metadata use a b:// uri string rather than the previously-recommended B berry objects.</span>
! Type
 
! Description
 
|-
 
| <code>author</code>
 
| string markdown
 
| Name of the creator for the code or content
 
|-
 
|  <code>title</code>
 
| string markdown
 
| Title of the content
 
|-
 
|  <code>source</code>
 
| string markdown
 
| URL where the content was found
 
|-  
 
|  <code>license</code>
 
| string markdown
 
| License for the code or content
 
 
|}
 
|}
  
 
+
<br><syntaxhighlight lang="javascript">
<p>Media stored on-chain using the [https://github.com/unwriter/B B://] protocol may be used as metadata for images, audio, or 3D models. To reference previously-uploaded data, use a string formatted either as&nbsp;<code>"b://&lt;txid&gt;"</code>&nbsp;or&nbsp;<code>"b://&lt;txid&gt;_o&lt;vout&gt;"</code>. If&nbsp;<code>vout</code>&nbsp;is present, it must be zero-indexed. If it is not present, the first B output in the transaction is used. To upload B data, you may use the [https://www.npmjs.com/package/@runonbitcoin/easy-b EasyB CLI tool]</p>
 
<p>.</p>
 
<p>You can start by setting these properties on your&nbsp;''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&nbsp;<code>metadata</code>&nbsp;you deem important, even properties that are not listed above.</p>
 
<span style="background-color: rgb(251, 238, 184);" >'''
 
<span style="color: rgb(186, 55, 42);" >Warning: For performance reasons, we recommended that B:// media metadata use a b:// uri string rather than the previously-recommended B berry objects.</span>'''</span><span style="background-color: rgb(186, 55, 42);"></span>
 
<syntaxhighlight lang="javascript">
 
 
class DigitalPet extends Jig { }
 
class DigitalPet extends Jig { }
  
Line 112: Line 116:
 
run.deploy(DigitalPet)
 
run.deploy(DigitalPet)
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
== Batch Transactions ==
 +
<div>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.</div><div><br></div><div>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</div><div><br></div><div>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.</div><div><br></div><div>Use two jigs separately in one transaction</div><div><br></div><div>run.transaction(() =&gt; {</div><div>&nbsp; new SomeJig()</div><div>&nbsp; anotherJig.send(address)</div><div>})</div><div><br></div><div>await run.sync()</div>
 +
== Caller Variable ==
 +
<div>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.</div><div><br></div><div>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.</div><div><br></div><div>Storing the parent jig</div><div><br></div><div>class Child extends Jig {</div><div>&nbsp; init() {</div><div>&nbsp; &nbsp; this.parent = caller</div><div>&nbsp; }</div><div>}</div><div>Enforcing a method may only be called by certain classes</div><div><br></div><div>class Database extends Jig {</div><div>&nbsp; init(rootUser) {</div><div>&nbsp; &nbsp; this.rootUser = rootUser</div><div>&nbsp; }</div><div><br></div><div>&nbsp; deleteAll() {</div><div>&nbsp; &nbsp; if (caller !== this.rootUser) throw new Error('only the root user may delete the database')</div><div>&nbsp; }</div><div>}</div><div><br></div><div>class User extends Jig {</div><div>&nbsp; deleteDatabase(database) {</div><div>&nbsp; &nbsp; database.deleteAll()</div><div>&nbsp; }</div><div>}</div><div></div>
 +
== Limiting Interactivity ==
 +
<div>By default, any jig may interact with any other jig in a transaction. Jigs may be atomically swapped, stored on each other, created from each other, and more. However, the downside 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 later. 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. This removes the risk that a jig might not be redeemable by a service after it interacts with other code that the service doesn't yet trust.</div><div><br></div><div>To limit interactivity, you can set the interactive property on your class or function to false. When code is non-interactive, it can only interact with itself, its instances, and any references it has to other code. Run enforces this.</div><div><br></div><div>Create a token that can only interact with itself</div><div><br></div><div>class MoneyBucks extends Token { }</div><div><br></div><div>MoneyBucks.interactive = false</div><div><br></div><div>run.deploy(MoneyBucks)</div><div></div>
 +
== Friends ==
 +
<div>To customize interactivity on classes with interactive = false, a convention to allow which Code your jigs can interact with is setting a friends array on the class definition.</div><div><br></div><div>Additionally, you may want to customize friends in the future, so implementing a static method to set friends after the initial deployment is a good idea.</div><div><br></div><div>class MoneyCoin extends Token {</div><div>&nbsp; static setFriends(friends) {</div><div>&nbsp; &nbsp; this.friends = friends;</div><div>&nbsp; }</div><div>}</div><div><br></div><div>MoneyCoin.friends = [friendsClass];</div>
 +
== Private Properties ==
 +
<div>Jigs may have private methods and private properties.</div><div><br></div><div>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.</div><div><br></div><div>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.</div><div><br></div><div>&nbsp;Warning: Private properties are not encrypted. They are a tool to control the surface API, not a tool for secrecy.</div><div><br></div><div>class TradingCard extends Jig {</div><div>&nbsp; send(to) {</div><div>&nbsp; &nbsp; this._checkNotBlacklisted(to)</div><div>&nbsp; &nbsp; this.owner = to</div><div>&nbsp; }</div><div><br></div><div>&nbsp; _checkNotBlacklisted(owner) {</div><div>&nbsp; &nbsp; if (TradingCard.blacklist.includes(owner)) {</div><div>&nbsp; &nbsp; &nbsp; throw new Error('blacklisted')</div><div>&nbsp; &nbsp; }</div><div>&nbsp; }</div><div>}</div><div><br></div><div>TradingCard.blacklist = [cheaterAddress]</div>
 +
== Auth Method ==
 +
<div>Every jig and every code has an auth() method on it. To auth means to spend a jig, forcing its owner to approve of any actions performed. Auth also ensures that the jig's state is the latest. You may use auth() for many purposes, such as to have a class approve of the creation of instances, as seen to the right. You may 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.</div><div><br></div><div>Limit the creation of jig instances by forcing the class to auth</div><div><br></div><div>class SingleTonMonster extends Jig {</div><div>&nbsp; init() {</div><div>&nbsp; &nbsp; SingleTonMonster.auth()</div><div>&nbsp; }</div><div>}</div>
 +
== Code Presets ==
 +
<div>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.</div><div><br></div><div>You can use presets by uploading 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.</div><div><br></div><div>class Dragon extends Jig {</div><div>&nbsp; set(name) {</div><div>&nbsp; &nbsp; this.name = name</div><div>&nbsp; }</div><div>}</div><div><br></div><div>Dragon.presets = {</div><div>&nbsp; main: {</div><div>&nbsp; &nbsp; origin: 'bee45c75c37a289517f33ebfa051601c9610ccc56fbddfbabc44413db5b0bc1b_o1',</div><div>&nbsp; &nbsp; location: 'bee45c75c37a289517f33ebfa051601c9610ccc56fbddfbabc44413db5b0bc1b_o1',</div><div>&nbsp; &nbsp; nonce: 1,</div><div>&nbsp; &nbsp; owner: '13amCautaFqwbWV6MoC86xrh96W4fXGfDV',</div><div>&nbsp; &nbsp; satoshis: 0</div><div>&nbsp; }</div><div>}</div>
 +
== Berries ==
 +
<div>Third-party protocols, like Twetch, B, and Metanet, may be used in Run as berries. Berries are JavaScript objects that deterministically capture data on the blockchain that is outside Run. If you can write code to parse an OP_RETURN, you can create Berries. Berries are more than data though. They are typed objects with methods, and they may be passed into jigs, stored as properties, cached, and more.</div><div><br></div><div>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.</div><div><br></div><div>To define a new Berry protocol, you create a class that extends from the base Berry class and implement the static pluck() method and init() method. Run calls your pluck() method to load data for that path into a JavaScript object. The second parameter to pluck is a method that 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. In pluck(), you instantiate a berry, which will call your init() method.</div><div><br></div><div>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: &lt;berry_class_location&gt;?berry=&lt;path&gt;&hash=&lt;state-hash&gt;&version=&lt;protocol&gt;. This is a unique identifier that allows them to be safely referenced and cached.</div><div><br></div><div>&nbsp;Note: If this seems complex, don't worry! We expect most users will not have to write their own berry classes. Like plugins, there will be many berry protocols to choose from in the future. Stay tuned.</div><div><br></div><div>Load a metanet node</div><div><br></div><div>const txid = '2f24d7edb8de0ef534d8e0bc2413eddda451b4accc481519a1647d7af79d8e88'</div><div>const node = await MetanetNode.load(txid)</div><div><br></div><div>Define the MetanetNode Berry</div><div><br></div><div>class MetanetNode extends Berry {</div><div>&nbsp; // Its constructor can only be called by its pluck function. Run guarantees this.</div><div>&nbsp; init(pnode, parent, data) {</div><div>&nbsp; &nbsp; this.pnode = pnode</div><div>&nbsp; &nbsp; this.parent = parent</div><div>&nbsp; &nbsp; this.data = data</div><div>&nbsp; }</div><div><br></div><div>&nbsp; // Run calls this pluck function when you call run.load with a Berry class.</div><div>&nbsp; static async pluck(location, fetch) {</div><div>&nbsp; &nbsp; const data = txo(await fetch(location))</div><div><br></div><div>&nbsp; &nbsp; if (data.out[0].s1 === 'meta') {</div><div>&nbsp; &nbsp; &nbsp; const pnode = data.out[0].s2</div><div>&nbsp; &nbsp; &nbsp; const txidParent = data.out[0].s3</div><div>&nbsp; &nbsp; &nbsp; const data = data.out[0].s4</div><div><br></div><div>&nbsp; &nbsp; &nbsp; if (data === 'METANET_ROOT') {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return new MetanetNode(pnode, null, data)</div><div>&nbsp; &nbsp; &nbsp; } else {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; const parentNode = await MetanetNode.load(txidParent)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return new MetanetNode(pnode, parentNode, data)</div><div>&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; }</div><div>}</div><div><br></div><div>MetanetNode.deps = { txo: Run.extra.txo }</div>
 +
== Locks ==
 +
<div>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.</div><div><br></div><div>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(). script() returns the bitcoin output script in hex format, and should be safe to call with any state. domain() returns the maximum size of the unlocking script for this lock. After implementing these methods, you set the owner of a jig to an instance of your lock, and Run will build its transaction output.</div><div><br></div><div>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 the Owner API requires two methods: sign() and nextOwner().</div><div><br></div><div>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.</div><div><br></div><div>&nbsp;Note: If this seems complex, don't worry! There will be many types of lock classes to choose from in the future, and most users won't need to write their own. Stay tuned.</div><div><br></div><div>Send a token to a P2PK output script</div><div><br></div><div>class PubkeyLock {</div><div>&nbsp; constructor(pubkey) { this.pubkey = pubkey }</div><div>&nbsp; script() { return asm(`${this.pubkey} OP_CHECKSIG`) }</div><div>&nbsp; domain() { return 74 }</div><div>}</div><div><br></div><div>PubkeyLock.deps = { asm }</div><div><br></div><div>token.send(new PubkeyLock(pubkey))</div><div>A provably unspendable lock</div><div><br></div><div>class Unspendable {</div><div>&nbsp; script() { return asm('OP_RETURN') }</div><div>&nbsp; domain() { return 0 }</div><div>}</div><div><br></div><div>Unspendable.deps = { asm }</div><div></div>
 +
= Where to go from here =
 +
Now, you can head over to [[Tips and Tricks]]
 +
 +
 +
[[Category:Documentation]]

Latest revision as of 22:08, 28 April 2023

Standard Metadata

Wallets, explorers, and exchanges will want to show your jigs. You can add special metadata to any jig to help these apps and services do so. Metadata is stored as an object called metadata on your jig, code, or berry, which contains various properties. The following common properties are called 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

string

Reference to an SVG or PNG image stored using B://

audio

string

Reference to an audio file stored using B://

glbModel

string

Reference to GLB file representing a 3d model 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

Media stored on-chain using the B:// protocol may be used as metadata for images, audio, or 3D models. To reference previously-uploaded data, use a string formatted either as "b://<txid>" or "b://<txid>_o<vout>". If vout is present, it must be zero-indexed. If it is not present, the first B output in the transaction is used. To upload B data, you may use the EasyB CLI tool.

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.

Warning: For performance reasons, we recommended that B:// media metadata use a b:// uri string rather than the previously-recommended B berry objects.


class DigitalPet extends Jig { }

DigitalPet.metadata = {
  author: 'Maximus',
  license: 'MIT',
  emoji: '🐉'
}

run.deploy(DigitalPet)

Batch Transactions

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.

Use two jigs separately in one transaction

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

await run.sync()

Caller Variable

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.

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()
  }
}

Limiting Interactivity

By default, any jig may interact with any other jig in a transaction. Jigs may be atomically swapped, stored on each other, created from each other, and more. However, the downside 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 later. 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. This removes the risk that a jig might not be redeemable by a service after it interacts with other code that the service doesn't yet trust.

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

Create a token that can only interact with itself

class MoneyBucks extends Token { }

MoneyBucks.interactive = false

run.deploy(MoneyBucks)

Friends

To customize interactivity on classes with interactive = false, a convention to allow which Code your jigs can interact with is setting a friends array on the class definition.

Additionally, you may want to customize friends in the future, so implementing a static method to set friends after the initial deployment is a good idea.

class MoneyCoin extends Token {
  static setFriends(friends) {
    this.friends = friends;
  }
}

MoneyCoin.friends = [friendsClass];

Private Properties

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.

 Warning: Private properties are not encrypted. They are a tool to control the surface API, not a tool for secrecy.

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]

Auth Method

Every jig and every code has an auth() method on it. To auth means to spend a jig, forcing its owner to approve of any actions performed. Auth also ensures that the jig's state is the latest. You may use auth() for many purposes, such as to have a class approve of the creation of instances, as seen to the right. You may 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.

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

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

Code Presets

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 presets by uploading 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.

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

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

Berries

Third-party protocols, like Twetch, B, and Metanet, may be used in Run as berries. Berries are JavaScript objects that deterministically capture data on the blockchain that is outside Run. If you can write code to parse an OP_RETURN, you can create Berries. Berries are more than data though. They are typed objects with methods, and 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 and init() method. Run calls your pluck() method to load data for that path into a JavaScript object. The second parameter to pluck is a method that 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. In pluck(), you instantiate a berry, which will call your init() method.

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.

 Note: If this seems complex, don't worry! We expect most users will not have to write their own berry classes. Like plugins, there will be many berry protocols to choose from in the future. Stay tuned.

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 }

Locks

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(). script() returns the bitcoin output script in hex format, and should be safe to call with any state. domain() returns the maximum size of the unlocking script for this lock. After implementing these methods, you set the owner of a jig to an instance of your lock, and Run will build its transaction output.

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 the Owner API requires 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.

 Note: If this seems complex, don't worry! There will be many types of lock classes to choose from in the future, and most users won't need to write their own. Stay tuned.

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 }

Where to go from here

Now, you can head over to Tips and Tricks