Skip to content
main.ts 3.74 KiB
Newer Older
Maciej Hirsz's avatar
Maciej Hirsz committed
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate RPC Proxy.
//
// Substrate RPC Proxy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

Maciej Hirsz's avatar
Maciej Hirsz committed
import * as express from 'express';
import { Request, Response } from 'express';
import { ApiPromise } from '@polkadot/api';
Maciej Hirsz's avatar
Maciej Hirsz committed
import { GenericAccountId } from '@polkadot/types/primitive';
Maciej Hirsz's avatar
Maciej Hirsz committed
import { BlockHash } from '@polkadot/types/interfaces/rpc';
Maciej Hirsz's avatar
Maciej Hirsz committed
import { HttpProvider, WsProvider } from '@polkadot/rpc-provider';
Maciej Hirsz's avatar
Maciej Hirsz committed

Maciej Hirsz's avatar
Maciej Hirsz committed
const HOST = process.env.BIND_HOST || '127.0.0.1';
Maciej Hirsz's avatar
Maciej Hirsz committed
const PORT = Number(process.env.BIND_PORT) || 8080;
Maciej Hirsz's avatar
Maciej Hirsz committed
const WS_URL = process.env.NODE_WS_URL || 'ws://127.0.0.1:9944';
Maciej Hirsz's avatar
Maciej Hirsz committed

async function main() {
Maciej Hirsz's avatar
Maciej Hirsz committed
	const api = await ApiPromise.create({ provider: new WsProvider(WS_URL) });
Maciej Hirsz's avatar
Maciej Hirsz committed
	const app = express();

Maciej Hirsz's avatar
Maciej Hirsz committed
	function handleError(res: Response): (err: any) => void {
		return (err) => {
			console.error('Internal Error:', err);

			res.status(500).send({ error: 'Interal Error' });
		}
	}

Maciej Hirsz's avatar
Maciej Hirsz committed
	async function fetchBlock(hash: BlockHash, req: Request, res: Response) {
Maciej Hirsz's avatar
Maciej Hirsz committed
		const { block } = await api.rpc.chain.getBlock(hash);
		const { parentHash, number, stateRoot, extrinsicsRoot } = block.header;

Maciej Hirsz's avatar
Maciej Hirsz committed
		const logs = block.header.digest.logs.map((log) => {
			const { type, index, value } = log;

			return { type, index, value };
		});
		const extrinsics = block.extrinsics.map((extrinsic) => {
			const { method, nonce, signature, signer, isSigned, args } = extrinsic;

			return {
				method: `${method.sectionName}.${method.methodName}`,
				signature: isSigned ? { signature, signer } : null,
				nonce,
				args,
			};
		});
Maciej Hirsz's avatar
Maciej Hirsz committed

		res.send({
			number,
			hash,
			parentHash,
			stateRoot,
			extrinsicsRoot,
			logs,
			extrinsics,
		});
	}

Maciej Hirsz's avatar
Maciej Hirsz committed
	async function fetchBalance(hash: BlockHash, address: string, req: Request, res: Response) {
		const [header, free, reserved, locks] = await Promise.all([
			api.rpc.chain.getHeader(hash),
			api.query.balances.freeBalance.at(hash, address),
			api.query.balances.reservedBalance.at(hash, address),
			api.query.balances.locks.at(hash, address),
		]);

		const at = {
			hash,
			height: header.number,
		};

		res.send({ at, free, reserved, locks });
	}

Maciej Hirsz's avatar
Maciej Hirsz committed
	app.get('/', (req, res) => res.send('Proxy is running, go to /block to get latest finalized block'));
Maciej Hirsz's avatar
Maciej Hirsz committed
	app.get('/block/:number', async (req, res) => {
		const number = Number(req.params.number) || 0;
		const hash = await api.rpc.chain.getBlockHash(number);

Maciej Hirsz's avatar
Maciej Hirsz committed
		await fetchBlock(hash, req, res).catch(handleError(res));
Maciej Hirsz's avatar
Maciej Hirsz committed
	});
Maciej Hirsz's avatar
Maciej Hirsz committed
	app.get('/block/', async (req, res) => {
		const hash = await api.rpc.chain.getFinalizedHead();

Maciej Hirsz's avatar
Maciej Hirsz committed
		await fetchBlock(hash, req, res).catch(handleError(res));
Maciej Hirsz's avatar
Maciej Hirsz committed
	});
Maciej Hirsz's avatar
Maciej Hirsz committed
	app.get('/balance/:address', async (req, res) => {
		const { address } = req.params;
		const hash = await api.rpc.chain.getFinalizedHead();

		await fetchBalance(hash, address, req, res).catch(handleError(res));
Maciej Hirsz's avatar
Maciej Hirsz committed
	});
Maciej Hirsz's avatar
Maciej Hirsz committed
	app.get('/balance/:address/:number', async (req, res) => {
		const { address } = req.params;
		const number = Number(req.params.number) || 0;
		const hash = await api.rpc.chain.getBlockHash(number);

		await fetchBalance(hash, address, req, res).catch(handleError(res));
Maciej Hirsz's avatar
Maciej Hirsz committed
	});
Maciej Hirsz's avatar
Maciej Hirsz committed

Maciej Hirsz's avatar
Maciej Hirsz committed
	app.listen(PORT, HOST, () => console.log(`Running on http://${HOST}:${PORT}/`))
Maciej Hirsz's avatar
Maciej Hirsz committed
}

main();