Skip to content

Commit

Permalink
feat(jsonrpc): add jsonrpc for v2 transition
Browse files Browse the repository at this point in the history
  • Loading branch information
kodemon committed Jul 27, 2023
1 parent c5b94d7 commit 6912522
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 4 deletions.
53 changes: 53 additions & 0 deletions packages/sdk/src/api/entities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export interface UnspentsEntity {
n: number;
txHash: string;
blockHash: string;
blockN: number;
sats: number;
scriptPubKey: ScriptPubKey;
txid: string;
value: number;
ordinals?: OrdinalsEntity[] | null;
inscriptions?: InscriptionsEntity[] | null;
safeToSpend: boolean;
confirmation: number;
}

export interface ScriptPubKey {
asm: string;
desc: string;
hex: string;
address: string;
type: string;
}

export interface OrdinalsEntity {
number: number;
decimal: string;
degree: string;
name: string;
height: number;
cycle: number;
epoch: number;
period: number;
offset: number;
rarity: string;
output: string;
start: number;
size: number;
}

export interface InscriptionsEntity {
id: string;
outpoint: string;
owner: string;
fee: number;
height: number;
number: number;
sat: number;
timestamp: number;
media_type: string;
media_size: number;
media_content: string;
meta?: Record<string, any>;
}
113 changes: 113 additions & 0 deletions packages/sdk/src/api/jsonrpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import fetch from "cross-fetch";

import { apiConfig } from "..";

class JsonRpc {
constructor(readonly url: string) {}

/**
* Send a JSON-RPC 2.0 notification to the connected Sado compliant server.
*
* @param method - Method to call.
* @param params - JSON-RPC 2.0 parameters.
*/
notify(method: string, params?: Params): void {
fetch(`${this.url}/rpc`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
jsonrpc: "2.0",
method,
params
})
});
}

async call<T>(method: string, id: Id): Promise<T>;
async call<T>(method: string, params: Params, id: Id): Promise<T>;
async call<T>(method: string, paramsOrId: Params | Id, id?: Id): Promise<T> {
let params: Params = {};
if (isJsonRpcId(paramsOrId)) {
id = paramsOrId;
} else {
params = paramsOrId;
}
const response = await fetch(`${this.url}/rpc`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
jsonrpc: "2.0",
method,
params,
id
})
});
if (response.status === 200) {
const json = await response.json();
if (json.error) {
throw new Error(json.error.message);
}
return json.result;
}
throw new Error(`Internal Server Error`);
}
}

/*
|--------------------------------------------------------------------------------
| RPC Clients
|--------------------------------------------------------------------------------
*/

export const rpc = {
get id() {
return Math.floor(Math.random() * 100000);
},
mainnet: new JsonRpc(getRpcUrl(apiConfig.apis.mainnet.batter)),
testnet: new JsonRpc(getRpcUrl(apiConfig.apis.testnet.batter)),
regtest: new JsonRpc(getRpcUrl(apiConfig.apis.regtest.batter))
} as const;

/*
|--------------------------------------------------------------------------------
| Utilities
|--------------------------------------------------------------------------------
*/

function isJsonRpcId(value: unknown): value is Id {
return isString(value) || isInteger(value) || value === null;
}

function isInteger(value: any): value is number {
return isNumber(value) && value % 1 === 0;
}

function isNumber(value: any): value is number {
const type = typeof value;
return type === "number" && value > Number.NEGATIVE_INFINITY && value < Number.POSITIVE_INFINITY;
}

function isString(value: any): value is string {
return typeof value === "string";
}

function getRpcUrl(value: string): string {
if (value[value.length - 1] === "/") {
return value.substring(0, value.length - 1);
}
return value;
}

/*
|--------------------------------------------------------------------------------
| Types
|--------------------------------------------------------------------------------
*/

type Id = string | number | null;

export type Params = unknown[] | Record<string, any>;
26 changes: 26 additions & 0 deletions packages/sdk/src/api/utxo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Network } from "../config/types";
import { InscriptionsEntity, UnspentsEntity } from "./entities";
import { rpc } from "./jsonrpc";

export async function getAllInscriptions({ address, network = "testnet" }: FetchInscriptionsOptions) {
const inscriptions: InscriptionsEntity[] = [];

const unspents = await rpc[network].call<UnspentsEntity[]>(
"GetUnspents",
{ address, options: { allowedrarity: ["common"] } },
rpc.id
);

for (const unspent of unspents) {
if (unspent.inscriptions && unspent.inscriptions.length) {
inscriptions.push(...unspent.inscriptions);
}
}

return inscriptions;
}

type FetchInscriptionsOptions = {
address: string;
network?: Network;
};
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6912522

Please sign in to comment.