Skip to content

Commit

Permalink
Clean up exports and interfaces (#58)
Browse files Browse the repository at this point in the history
* Clean up exports and interfaces

* Final commit to release with documentation
  • Loading branch information
summraznboi authored Apr 24, 2024
1 parent 5ea8999 commit bdeb2d8
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 8 deletions.
117 changes: 116 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,118 @@
# runestone-lib

A Typescript implementation of the Bitcoin Runestone protocol
This is a Typescript implementation of the Bitcoin Runestone protocol.
To see the original version, please go to the [Ordinals repo](/ordinals/ord);
you can find various [data structures](/ordinals/ord/tree/master/crates/ordinals/src) and
[indexer implementation](/ordinals/ord/blob/master/src/index/updater/rune_updater.rs) there.
General documentation of the runes protocol and how runestones are used can be found
[here](https://docs.ordinals.com/runes.html).

## Encode Runestone

To encode a runestone, use `encodeRunestone()` method, with an example below:

```ts
import { encodeRunestone } from '@magiceden-oss/runestone-lib';

// To deploy a new rune ticker
// (this will require a commitment in an input script)
const etchingRunestone = encodeRunestone({
etching: {
runeName: 'THIS•IS•AN•EXAMPLE•RUNE',
divisibility: 0,
premine: 0,
symbol: '',
terms: {
cap: 69,
amount: 420,
offset: {
end: 9001,
},
},
turbo: true,
},
});

// To mint UNCOMMON•GOODS
const mintRunestone = encodeRunestone({
mint: {
block: 1n,
tx: 0,
},
});

// Transfer 10 UNCOMMON•GOODS to output 1
const edictRunestone = encodeRunestone({
edicts: [
{
id: {
block: 1n,
tx: 0,
},
amount: 10n,
output: 1,
},
],
});
```

## Decode Runestone

Decoding a runestone within a transaction is as simple as passing in
the transaction data from Bitcoin Core RPC server.

```ts
import {
tryDecodeRunestone,
isRunestoneArtifact,
RunestoneSpec,
Cenotaph
} from '@magiceden-oss/runestone-lib';

// transaction retrieved with getrawtransaction RPC call
const tx = ...;

const artifact = tryDecodeRunestone(tx);

if (isRunestone(artifact)) {
const runestone: RunestoneSpec = artifact;
...
} else {
const cenotaph: Cenotaph = artifact;
...
}
```

## Indexing

To index, initialize a RunestoneIndexer, implement the interface arguments
to RunestoneIndexer constructor. Then it is just a matter of start() to finish
initializing the indexer, and then controlling the rate of syncing indexing
to latest state in RPC server.

```ts
// Initialize indexer
const indexer = new RunestoneIndexer(...);

// Preps the indexer to be ready to run updateRuneUtxoBalances()
await indexer.start()

// Example of a polling job running updateRuneUtxoBalances()
// every minute, with stop cleanup handling
let stop = false;
...

const intervalId = setInterval(async () => {
try {
await index.updateRuneUtxoBalances();
} catch (err) {
console.error('Error occurred while indexing runes', err);
}

if (stop) {
clearInterval(intervalId);
await indexer.stop();
}
}, 60 * 1000 /* one minute */);

```
50 changes: 46 additions & 4 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isRunestone } from './src/artifact';
import { isRunestone as isRunestoneArtifact } from './src/artifact';
import { MAX_DIVISIBILITY } from './src/constants';
import { Etching } from './src/etching';
import { Flaw as FlawEnum } from './src/flaw';
import { RuneEtchingSpec } from './src/indexer';
import { u128, u32, u64, u8 } from './src/integer';
import { None, Option, Some } from './src/monads';
Expand Down Expand Up @@ -56,15 +57,52 @@ export type RunestoneSpec = {
}[];
};

export type Flaw =
| 'edict_output'
| 'edict_rune_id'
| 'invalid_script'
| 'opcode'
| 'supply_overflow'
| 'trailing_integers'
| 'truncated_field'
| 'unrecognized_even_tag'
| 'unrecognized_flag'
| 'varint';

export type Cenotaph = {
flaws: string[];
flaws: Flaw[];
etching?: string;
mint?: {
block: bigint;
tx: number;
};
};

function getFlawString(flaw: FlawEnum): Flaw {
switch (flaw) {
case FlawEnum.EDICT_OUTPUT:
return 'edict_output';
case FlawEnum.EDICT_RUNE_ID:
return 'edict_rune_id';
case FlawEnum.INVALID_SCRIPT:
return 'invalid_script';
case FlawEnum.OPCODE:
return 'opcode';
case FlawEnum.SUPPLY_OVERFLOW:
return 'supply_overflow';
case FlawEnum.TRAILING_INTEGERS:
return 'trailing_integers';
case FlawEnum.TRUNCATED_FIELD:
return 'truncated_field';
case FlawEnum.UNRECOGNIZED_EVEN_TAG:
return 'unrecognized_even_tag';
case FlawEnum.UNRECOGNIZED_FLAG:
return 'unrecognized_flag';
case FlawEnum.VARINT:
return 'varint';
}
}

// Helper functions to ensure numbers fit the desired type correctly
const u8Strict = (n: number) => {
const bigN = BigInt(n);
Expand Down Expand Up @@ -195,14 +233,18 @@ export function encodeRunestone(runestone: RunestoneSpec): {
};
}

export function isRunestone(artifact: RunestoneSpec | Cenotaph): artifact is RunestoneSpec {
return !('flaws' in artifact);
}

export function tryDecodeRunestone(tx: RunestoneTx): RunestoneSpec | Cenotaph | null {
const optionArtifact = Runestone.decipher(tx);
if (optionArtifact.isNone()) {
return null;
}

const artifact = optionArtifact.unwrap();
if (isRunestone(artifact)) {
if (isRunestoneArtifact(artifact)) {
const runestone = artifact;

const etching = () => runestone.etching.unwrap();
Expand Down Expand Up @@ -286,7 +328,7 @@ export function tryDecodeRunestone(tx: RunestoneTx): RunestoneSpec | Cenotaph |
} else {
const cenotaph = artifact;
return {
flaws: [],
flaws: cenotaph.flaws.map(getFlawString),
...(cenotaph.etching.isSome() ? { etching: cenotaph.etching.unwrap().toString() } : {}),
...(cenotaph.mint.isSome()
? { mint: { block: cenotaph.mint.unwrap().block, tx: Number(cenotaph.mint.unwrap().tx) } }
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@magiceden-oss/runestone-lib",
"version": "0.9.10-alpha",
"version": "1.0.0",
"description": "",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
2 changes: 0 additions & 2 deletions src/indexer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ export interface RunestoneStorage {

export type RunestoneIndexerOptions = {
bitcoinRpcClient: BitcoinRpcClient;

network: Network;

storage: RunestoneStorage;
};

Expand Down

0 comments on commit bdeb2d8

Please sign in to comment.