Difference between revisions of "Jigs"
| Line 52: | Line 52: | ||
== Syncing == | == Syncing == | ||
| + | |||
| + | 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. | ||
| + | |||
| + | Every jig has <code>sync()</code> available to ensure your local state is the same as that on the network. Calling <code>await jig.sync()</code> returns when: | ||
| + | |||
| + | All pending local transactions have been published successfully, and | ||
| + | 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 <code>sync()</code> 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 <code>sync()</code> after every method call to ensure that you catch errors early and handle them gracefully. Calling <code>sync()</code> will force any error to show up at that particular line in the code. | ||
| + | |||
| + | 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. | ||
=== Code === | === Code === | ||
| Line 79: | Line 91: | ||
== Interactivity == | == Interactivity == | ||
| + | |||
| + | 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 <code>new</code> 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 <code>deps</code> 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 [https://run.network/docs/#how-it-works-ownership-rules Ownership Rules]. | ||
=== Code === | === Code === | ||
| Line 95: | Line 113: | ||
== Destroying == | == Destroying == | ||
| + | |||
| + | Not all jigs will live forever. You may need to clean up old jigs in your inventory, and or you may need to have one jig consume another jig to make an update. Calling <code>destroy()</code> on a jig creates a transaction that removes the jig's output and makes it un-updatable. To do this, a jig may be destroyed. When a jig is destroyed, it no longer has an unspent output on the blockchain, and it will not appear in your inventory. | ||
| + | |||
| + | Destroyed jigs still have unique locations that identify their final state. You can even still load their final state using <code>run.load()</code>. However, destroyed jigs are marked as being destroyed with a <code>null</code> owner and they are forever locked in their final state. | ||
| + | |||
| + | 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 <code>destroy()</code> to put the jig into its final state. | ||
=== Code === | === Code === | ||
| Line 120: | Line 144: | ||
== Backing == | == Backing == | ||
| + | |||
| + | 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 <code>satoshis</code> property to a number in a method. Your purse will automatically deposit that amount into the jig. When the <code>satoshis</code> property is later decreased, those Bitcoins will be withdrawn to the active 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. | ||
| + | |||
| + | <pre> | ||
| + | 🛈 Note: All bitcoin outputs must have an amount at least the dust limit to be accepted by the network. Run will choose the greater of your jig's satoshis property and this dust limit when building your jig's transaction output. | ||
| + | </pre> | ||
=== Code === | === Code === | ||
Revision as of 23:13, 14 December 2022
Jigs are interactive objects on Bitcoin. You define a jig with a JavaScript class and that class determines exactly what the jig can do. All of your jigs are 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.
Contents
Creating
Let's create a new class of jigs called Post to represent comments on a message board. In JavaScript, your class initializer 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.
Code
class Post extends Jig {
init(message) {
this.message = message
}
}
new Post('Hello, world')
Updating
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.
Code
class EditablePost extends Post {
edit(message) {
this.message = message
}
}
const editablePost = new EditablePost('Hello, world')
editablePost.edit('Hello, BitCoin')
Sending
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.
Code
class Dragon extends Jig {
send(to) {
this.owner = to
}
}
new Dragon().send(address)
Syncing
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.
Every jig has sync() available to ensure your local state is the same as that on the network. Calling await jig.sync() returns when:
All pending local transactions have been published successfully, and 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.
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.
Code
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()
Interactivity
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.
Code
class Event extends Jig {
createTicket() { return new Ticket(this) }
}
class Ticket extends Jig {
init(event) { this.event = event }
}
Event.deps = { Ticket }
Destroying
Not all jigs will live forever. You may need to clean up old jigs in your inventory, and or you may need to have one jig consume another jig to make an update. Calling destroy() on a jig creates a transaction that removes the jig's output and makes it un-updatable. To do this, a jig may be destroyed. When a jig is destroyed, it no longer has an unspent output on the blockchain, and it will not appear in your inventory.
Destroyed jigs still have unique locations that identify their final state. You can even still load their final state using run.load(). However, destroyed jigs are marked as being destroyed with a null owner and they are forever locked in their final state.
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.
Code
Destroy a jig
giftCard.destroy()
Override destroy to finalize a jig
class GiftCard extends Jig {
init(value) {
this.value = value
}
destroy() {
super.destroy()
this.value = 0
}
}
Backing
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 in a method. Your purse will automatically deposit that amount into the jig. When the satoshis property is later decreased, those Bitcoins will be withdrawn to the active 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.
🛈 Note: All bitcoin outputs must have an amount at least the dust limit to be accepted by the network. Run will choose the greater of your jig's satoshis property and this dust limit when building your jig's transaction output.
Code
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)
Checking Parameters
Code
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 }
Attaching Media
Code
class DinoPet extends Jig { }
DinoPet.metadata = {
emoji: '🦖',
image: 'b://55e2c09672355e009e4727c2365fb61d12c69add91215ee3e9f50aa76c808536'
}
new DinoPet()