Skip to content

Instantly share code, notes, and snippets.

@tgrecojs
Created January 10, 2024 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tgrecojs/4e4a64394b68b32dc86ffe1326268d74 to your computer and use it in GitHub Desktop.
Save tgrecojs/4e4a64394b68b32dc86ffe1326268d74 to your computer and use it in GitHub Desktop.
/**
* @file endo plugin for cosmos network protocol primitives: RPC, LCD (aka API).
*/
// @ts-check
/* global globalThis */
import { Far } from '@endo/far';
import { makeHttpClient, makeLCD } from './httpClient.js';
assert.typeof(globalThis.fetch, 'function');
export const make = _powers => {
/** @param { string } rpcURL */
const makeRPCClient = rpcURL => makeHttpClient(rpcURL, globalThis.fetch);
/** @param { string } apiURL */
const makeLCDClient = apiURL => makeLCD(apiURL, { fetch: globalThis.fetch });
return Far('CosmosFetch', {
makeRPCClient,
makeLCDClient,
});
};
/**
* @file cosmos network primitives with explicit `fetch` network access.
*/
// @ts-check
import { Far } from '@endo/far';
const { freeze } = Object;
const filterBadStatus = res => {
if (res.status >= 400) {
throw new Error(`Bad status on response: ${res.status}`);
}
return res;
};
/**
* Make an RpcClient using explicit access to the network.
*
* The RpcClient implementations included in cosmjs
* such as {@link https://cosmos.github.io/cosmjs/latest/tendermint-rpc/classes/HttpClient.html HttpClient}
* use ambient authority (fetch or axios) for network access.
*
* To facilitate cooperation without vulnerability,
* as well as unit testing, etc. this RpcClient maker takes
* network access as a parameter, following
* {@link https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline|OCap Discipline}.
*
* @param {string} url
* @param {typeof window.fetch} fetch
* @returns {import('@cosmjs/tendermint-rpc').RpcClient}
*/
export const makeHttpClient = (url, fetch) => {
// cribbed from @agoric/casting/src/makeHttpClient.js
const headers = {}; // XXX needed?
// based on cosmjs 0.30.1:
// https://github.com/cosmos/cosmjs/blob/33271bc51cdc865cadb647a1b7ab55d873637f39/packages/tendermint-rpc/src/rpcclients/http.ts#L37
// https://github.com/cosmos/cosmjs/blob/33271bc51cdc865cadb647a1b7ab55d873637f39/packages/tendermint-rpc/src/rpcclients/httpclient.ts#L25
return Far('RpcClient', {
disconnect: () => {
// nothing to be done
},
/**
* @param {import('@cosmjs/json-rpc').JsonRpcRequest} request
*/
execute: async request => {
const settings = {
method: 'POST',
body: request ? JSON.stringify(request) : undefined,
headers: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
...headers,
},
};
return fetch(url, settings)
.then(filterBadStatus)
.then(res => res.json());
},
});
};
/**
* @param {string} apiURL
* @param {object} io
* @param {typeof fetch} io.fetch
*/
export const makeLCD = (apiURL, { fetch }) => {
assert.typeof(apiURL, 'string');
/**
* @param {string} href
* @param {object} [options]
* @param {Record<string, string>} [options.headers]
*/
const getJSON = (href, options = {}) => {
const opts = {
keepalive: true,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
};
const url = `${apiURL}${href}`;
return fetch(url, opts).then(r => {
if (!r.ok) throw Error(r.statusText);
return r.json().then(data => {
return data;
});
});
};
return Far('LCD', {
getJSON,
latestBlock: () => getJSON(`/cosmos/base/tendermint/v1beta1/blocks/latest`),
});
};
/** @typedef {ReturnType<makeLCD>} LCD */
/**
* Usage:
* $ endo make --UNSAFE src/cosmosFetch.js -n cosmos-fetch
* Object [Alleged: CosmosFetch] {}
* $ endo make test/net-local.js -n local -p cosmos-fetch
* { lcd: Object [Alleged: LCD] {}, rpc: Object [Alleged: RpcClient] {} }
*/
import { E } from '@endo/far';
const loc = {
lcd: 'http://localhost:1317',
rpc: 'http://localhost:26657',
};
export const make = async net => {
const [lcd, rpc] = await Promise.all([
E(net).makeLCDClient(loc.lcd),
E(net).makeRPCClient(loc.rpc),
]);
return harden({ lcd, rpc });
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment