Skip to content

Commit bdeb2d8

Browse files
authored
Clean up exports and interfaces (#58)
* Clean up exports and interfaces * Final commit to release with documentation
1 parent 5ea8999 commit bdeb2d8

File tree

4 files changed

+163
-8
lines changed

4 files changed

+163
-8
lines changed

README.md

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,118 @@
11
# runestone-lib
22

3-
A Typescript implementation of the Bitcoin Runestone protocol
3+
This is a Typescript implementation of the Bitcoin Runestone protocol.
4+
To see the original version, please go to the [Ordinals repo](/ordinals/ord);
5+
you can find various [data structures](/ordinals/ord/tree/master/crates/ordinals/src) and
6+
[indexer implementation](/ordinals/ord/blob/master/src/index/updater/rune_updater.rs) there.
7+
General documentation of the runes protocol and how runestones are used can be found
8+
[here](https://docs.ordinals.com/runes.html).
9+
10+
## Encode Runestone
11+
12+
To encode a runestone, use `encodeRunestone()` method, with an example below:
13+
14+
```ts
15+
import { encodeRunestone } from '@magiceden-oss/runestone-lib';
16+
17+
// To deploy a new rune ticker
18+
// (this will require a commitment in an input script)
19+
const etchingRunestone = encodeRunestone({
20+
etching: {
21+
runeName: 'THIS•IS•AN•EXAMPLE•RUNE',
22+
divisibility: 0,
23+
premine: 0,
24+
symbol: '',
25+
terms: {
26+
cap: 69,
27+
amount: 420,
28+
offset: {
29+
end: 9001,
30+
},
31+
},
32+
turbo: true,
33+
},
34+
});
35+
36+
// To mint UNCOMMON•GOODS
37+
const mintRunestone = encodeRunestone({
38+
mint: {
39+
block: 1n,
40+
tx: 0,
41+
},
42+
});
43+
44+
// Transfer 10 UNCOMMON•GOODS to output 1
45+
const edictRunestone = encodeRunestone({
46+
edicts: [
47+
{
48+
id: {
49+
block: 1n,
50+
tx: 0,
51+
},
52+
amount: 10n,
53+
output: 1,
54+
},
55+
],
56+
});
57+
```
58+
59+
## Decode Runestone
60+
61+
Decoding a runestone within a transaction is as simple as passing in
62+
the transaction data from Bitcoin Core RPC server.
63+
64+
```ts
65+
import {
66+
tryDecodeRunestone,
67+
isRunestoneArtifact,
68+
RunestoneSpec,
69+
Cenotaph
70+
} from '@magiceden-oss/runestone-lib';
71+
72+
// transaction retrieved with getrawtransaction RPC call
73+
const tx = ...;
74+
75+
const artifact = tryDecodeRunestone(tx);
76+
77+
if (isRunestone(artifact)) {
78+
const runestone: RunestoneSpec = artifact;
79+
...
80+
} else {
81+
const cenotaph: Cenotaph = artifact;
82+
...
83+
}
84+
```
85+
86+
## Indexing
87+
88+
To index, initialize a RunestoneIndexer, implement the interface arguments
89+
to RunestoneIndexer constructor. Then it is just a matter of start() to finish
90+
initializing the indexer, and then controlling the rate of syncing indexing
91+
to latest state in RPC server.
92+
93+
```ts
94+
// Initialize indexer
95+
const indexer = new RunestoneIndexer(...);
96+
97+
// Preps the indexer to be ready to run updateRuneUtxoBalances()
98+
await indexer.start()
99+
100+
// Example of a polling job running updateRuneUtxoBalances()
101+
// every minute, with stop cleanup handling
102+
let stop = false;
103+
...
104+
105+
const intervalId = setInterval(async () => {
106+
try {
107+
await index.updateRuneUtxoBalances();
108+
} catch (err) {
109+
console.error('Error occurred while indexing runes', err);
110+
}
111+
112+
if (stop) {
113+
clearInterval(intervalId);
114+
await indexer.stop();
115+
}
116+
}, 60 * 1000 /* one minute */);
117+
118+
```

index.ts

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { isRunestone } from './src/artifact';
1+
import { isRunestone as isRunestoneArtifact } from './src/artifact';
22
import { MAX_DIVISIBILITY } from './src/constants';
33
import { Etching } from './src/etching';
4+
import { Flaw as FlawEnum } from './src/flaw';
45
import { RuneEtchingSpec } from './src/indexer';
56
import { u128, u32, u64, u8 } from './src/integer';
67
import { None, Option, Some } from './src/monads';
@@ -56,15 +57,52 @@ export type RunestoneSpec = {
5657
}[];
5758
};
5859

60+
export type Flaw =
61+
| 'edict_output'
62+
| 'edict_rune_id'
63+
| 'invalid_script'
64+
| 'opcode'
65+
| 'supply_overflow'
66+
| 'trailing_integers'
67+
| 'truncated_field'
68+
| 'unrecognized_even_tag'
69+
| 'unrecognized_flag'
70+
| 'varint';
71+
5972
export type Cenotaph = {
60-
flaws: string[];
73+
flaws: Flaw[];
6174
etching?: string;
6275
mint?: {
6376
block: bigint;
6477
tx: number;
6578
};
6679
};
6780

81+
function getFlawString(flaw: FlawEnum): Flaw {
82+
switch (flaw) {
83+
case FlawEnum.EDICT_OUTPUT:
84+
return 'edict_output';
85+
case FlawEnum.EDICT_RUNE_ID:
86+
return 'edict_rune_id';
87+
case FlawEnum.INVALID_SCRIPT:
88+
return 'invalid_script';
89+
case FlawEnum.OPCODE:
90+
return 'opcode';
91+
case FlawEnum.SUPPLY_OVERFLOW:
92+
return 'supply_overflow';
93+
case FlawEnum.TRAILING_INTEGERS:
94+
return 'trailing_integers';
95+
case FlawEnum.TRUNCATED_FIELD:
96+
return 'truncated_field';
97+
case FlawEnum.UNRECOGNIZED_EVEN_TAG:
98+
return 'unrecognized_even_tag';
99+
case FlawEnum.UNRECOGNIZED_FLAG:
100+
return 'unrecognized_flag';
101+
case FlawEnum.VARINT:
102+
return 'varint';
103+
}
104+
}
105+
68106
// Helper functions to ensure numbers fit the desired type correctly
69107
const u8Strict = (n: number) => {
70108
const bigN = BigInt(n);
@@ -195,14 +233,18 @@ export function encodeRunestone(runestone: RunestoneSpec): {
195233
};
196234
}
197235

236+
export function isRunestone(artifact: RunestoneSpec | Cenotaph): artifact is RunestoneSpec {
237+
return !('flaws' in artifact);
238+
}
239+
198240
export function tryDecodeRunestone(tx: RunestoneTx): RunestoneSpec | Cenotaph | null {
199241
const optionArtifact = Runestone.decipher(tx);
200242
if (optionArtifact.isNone()) {
201243
return null;
202244
}
203245

204246
const artifact = optionArtifact.unwrap();
205-
if (isRunestone(artifact)) {
247+
if (isRunestoneArtifact(artifact)) {
206248
const runestone = artifact;
207249

208250
const etching = () => runestone.etching.unwrap();
@@ -286,7 +328,7 @@ export function tryDecodeRunestone(tx: RunestoneTx): RunestoneSpec | Cenotaph |
286328
} else {
287329
const cenotaph = artifact;
288330
return {
289-
flaws: [],
331+
flaws: cenotaph.flaws.map(getFlawString),
290332
...(cenotaph.etching.isSome() ? { etching: cenotaph.etching.unwrap().toString() } : {}),
291333
...(cenotaph.mint.isSome()
292334
? { mint: { block: cenotaph.mint.unwrap().block, tx: Number(cenotaph.mint.unwrap().tx) } }

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@magiceden-oss/runestone-lib",
3-
"version": "0.9.10-alpha",
3+
"version": "1.0.0",
44
"description": "",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

src/indexer/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,7 @@ export interface RunestoneStorage {
7171

7272
export type RunestoneIndexerOptions = {
7373
bitcoinRpcClient: BitcoinRpcClient;
74-
7574
network: Network;
76-
7775
storage: RunestoneStorage;
7876
};
7977

0 commit comments

Comments
 (0)