Skip to content

Commit

Permalink
chore: Add CIP68 Deno example (#562)
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega committed Apr 13, 2023
1 parent 41bd22b commit 89eab0f
Show file tree
Hide file tree
Showing 13 changed files with 516 additions and 87 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ config = { version = "0.13.2", default-features = false, features = [
"json",
] }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.89", features = ["arbitrary_precision"] }
serde_json = { version = "1.0.89" }
strum = "0.24"
strum_macros = "0.24"
prometheus_exporter = { version = "0.8.5", default-features = false }
Expand Down
17 changes: 0 additions & 17 deletions examples/deno/daemon.toml

This file was deleted.

12 changes: 0 additions & 12 deletions examples/deno/mapper.js

This file was deleted.

1 change: 1 addition & 0 deletions examples/deno_cip68/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.js
76 changes: 76 additions & 0 deletions examples/deno_cip68/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# CIP68 Parser Example

This example shows how to leverage the Deno filter stage to apply custom parsing logic and extract CIP68 reference NFT data from transaction without the need to change Oura's internal processing pipeline.

## Configuration

The relevant section of the daemon.toml is the following:

```toml
[[filters]]
type = "Deno"
main_module = "./parser.js"
```

The above configuration fragment instructs _Oura_ to introduce a _Deno_ filter that uses the logic specified in the file `parser.js`, which holds your custom filter logic.

The following section explains how to create a .js file compatible with what the Deno filter is expecting.

## Custom Deno Filter

To create the custom logic for your Deno-base filter, you need to start by creating a _Typescript_ and implementing a `mapEvent` function:

```ts
export function mapEvent(record: oura.Event) {
// your custom logic goes here
}
```

The above function will be called for each record ths goes through _Oura_'s pipeline. Depending on your configuration, records could represent blocks, transactions or other payloads generated by previous filter stages.

## CIP68

The goal for this particular example is to extract data from transactions that corresponds to reference NFT as defined by [CIP68](https://cips.cardano.org/cips/cip68).

By inspecting the outputs and Plutus datums from the transactions, we can parse relevant information and generate custom JSON objects such as this one:

```json
{
"label": "000643b042756438363031",
"policy": "4523c5e21d409b81c95b45b0aea275b8ea1406e6cafea5583b9f8a5f",
"metadata": {
"name": "SpaceBud #8601",
"traits": "",
"type": "Shark",
"image": "ipfs://bafkreidrqwxpxhyc5bo364fzzwv7nhnjel6y6zkywriw33jopb2p4tba5u",
"sha256": "7185aefb9f02e85dbf70b9cdabf69da922fd8f6558b4516ded2e7874fe4c20ed"
},
"version": 1,
"txHash": "720dd358d2b28531e181f93eed0e0d24db364232ddaccf6655abf92790a062d5"
}
```

You can check the actual code for the parser in the file `parser.ts`. Without going much into detail, the relevant part of the parser is a function called `extractRefNFT`:

```ts
function extractNFTRef(
output: oura.TxOutputRecord,
allDatums?: oura.PlutusDatumRecord[] | null
): Partial<RefNFT> | null {
const asset = output.assets?.find((a) => a.asset.startsWith("000"));

if (!asset) return null;

const datum =
output.inline_datum ||
allDatums?.find((d) => d.datum_hash == output.datum_hash);

if (!datum) return null;

return {
label: asset.asset,
policy: asset.policy,
...parseDatum(datum),
};
}
```
21 changes: 21 additions & 0 deletions examples/deno_cip68/daemon.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[source]
type = "N2N"
peers = ["relays-new.cardano-mainnet.iohk.io:3001"]

[intersect]
type = "Point"
value = [
87938927,
"7d6f25ff981bdc92f6b61c04d7f9d0b7236783da9c66b2d0ba5dc6953b68b34f",
]

[[filters]]
type = "LegacyV1"
include_transaction_details = true

[[filters]]
type = "Deno"
main_module = "./parser.js"

[sink]
type = "Noop"
254 changes: 254 additions & 0 deletions examples/deno_cip68/ouraTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
export type Era =
| "Undefined"
| "Unknown"
| "Byron"
| "Shelley"
| "Allegra"
| "Mary"
| "Alonzo"
| "Babbage";

export type GenericJson = Record<string, unknown>;

export type MetadatumRendition = {
map_json?: GenericJson;
array_json?: GenericJson;
int_scalar?: string;
text_scalar?: string;
bytes_hex?: string;
};

export type MetadataRecord = {
label: string;
content: MetadatumRendition;
};

export type CIP25AssetRecord = {
version: string;
policy: string;
asset: string;
name: string | null;
image: string | null;
media_type: string | null;
description: string | null;
raw_json: GenericJson;
};

export type CIP15AssetRecord = {
voting_key: string;
stake_pub: string;
reward_address: string;
nonce: number;
raw_json: GenericJson;
};

export type TxInputRecord = {
tx_id: string;
index: number;
};

export type OutputAssetRecord = {
policy: string;
asset: string;
asset_ascii: string | null;
amount: number;
};

export type TxOutputRecord = {
address: string;
amount: number;
assets: OutputAssetRecord[] | null;
datum_hash: string | null;
inline_datum: PlutusDatumRecord | null;
};

export type MintRecord = {
policy: string;
asset: string;
quantity: number;
};

export type WithdrawalRecord = {
reward_account: string;
coin: number;
};

export type TransactionRecord = {
hash: string;
fee: number;
ttl: number | null;
validity_interval_start: number | null;
network_id: number | null;
input_count: number;
collateral_input_count: number;
has_collateral_output: boolean;
output_count: number;
mint_count: number;
total_output: number;

// include_details
metadata: MetadataRecord[] | null;
inputs: TxInputRecord[] | null;
outputs: TxOutputRecord[] | null;
collateral_inputs: TxInputRecord[] | null;
collateral_output: TxOutputRecord | null;
mint: MintRecord[] | null;
vkey_witnesses: VKeyWitnessRecord[] | null;
native_witnesses: NativeWitnessRecord[] | null;
plutus_witnesses: PlutusWitnessRecord[] | null;
plutus_redeemers: PlutusRedeemerRecord[] | null;
plutus_data: PlutusDatumRecord[] | null;
withdrawals: WithdrawalRecord[] | null;
size: number;
};

export type EventContext = {
block_hash: string | null;
block_number: number | null;
slot: number | null;
timestamp: number | null;
tx_idx: number | null;
tx_hash: string | null;
input_idx: number | null;
output_idx: number | null;
output_address: string | null;
certificate_idx: number | null;
};

export type StakeCredential = {
addr_keyhash?: string;
scripthash?: string;
};

export type VKeyWitnessRecord = {
vkey_hex: string;
signature_hex: string;
};

export type NativeWitnessRecord = {
policy_id: string;
script_json: GenericJson;
};

export type PlutusWitnessRecord = {
script_hash: string;
script_hex: string;
};

export type PlutusRedeemerRecord = {
purpose: string;
ex_units_mem: number;
ex_units_steps: number;
input_idx: number;
plutus_data: GenericJson;
};

export type PlutusDatumRecord = {
datum_hash: string;
plutus_data: GenericJson;
};

export type BlockRecord = {
era: Era;
epoch: number | null;
epoch_slot: number | null;
body_size: number;
issuer_vkey: string;
vrf_vkey: string;
tx_count: number;
slot: number;
hash: string;
number: number;
previous_hash: string;
cbor_hex: string | null;
transactions: TransactionRecord[] | null;
};

export type CollateralRecord = {
tx_id: string;
index: number;
};

export type PoolRegistrationRecord = {
operator: string;
vrf_keyhash: string;
pledge: number;
cost: number;
margin: number;
reward_account: string;
pool_owners: string[];
relays: string[];
pool_metadata: string | null;
pool_metadata_hash: string | null;
};

export type RollBackRecord = {
block_slot: number;
block_hash: string;
};

export type MoveInstantaneousRewardsCertRecord = {
from_reserves: boolean;
from_treasury: boolean;
to_stake_credentials: Array<[StakeCredential, number]> | null;
to_other_pot: number | null;
};

export type NativeScriptRecord = {
policy_id: string;
script: GenericJson;
};

export type PlutusScriptRecord = {
hash: string;
data: string;
};

export type StakeRegistrationRecord = { credential: StakeCredential };

export type StakeDeregistrationRecord = { credential: StakeCredential };

export type StakeDelegation = {
credential: StakeCredential;
pool_hash: string;
};

export type PoolRetirementRecord = {
pool: string;
epoch: number;
};

export type GenesisKeyDelegationRecord = {};

export type Event = {
context: EventContext;
fingerprint?: string;

block?: BlockRecord;
block_end?: BlockRecord;
transaction?: TransactionRecord;
transaction_end?: TransactionRecord;
tx_input?: TxInputRecord;
tx_output?: TxOutputRecord;
output_asset?: OutputAssetRecord;
metadata?: MetadataRecord;
v_key_witness?: VKeyWitnessRecord;
native_witness?: NativeWitnessRecord;
plutus_witness?: PlutusWitnessRecord;
plutus_redeemer?: PlutusRedeemerRecord;
plutus_datum?: PlutusDatumRecord;
cip25_asset?: CIP25AssetRecord;
cip15_asset?: CIP15AssetRecord;
mint?: MintRecord;
collateral?: CollateralRecord;
native_script?: NativeScriptRecord;
plutus_script?: PlutusScriptRecord;
stake_registration?: StakeRegistrationRecord;
stake_deregistration?: StakeDeregistrationRecord;
stake_delegation?: StakeDelegation;
pool_registration?: PoolRegistrationRecord;
pool_retirement?: PoolRetirementRecord;
genesis_key_delegation?: GenesisKeyDelegationRecord;
move_instantaneous_rewards_cert?: MoveInstantaneousRewardsCertRecord;
roll_back?: RollBackRecord;
};
Loading

0 comments on commit 89eab0f

Please sign in to comment.