dimension-js-sdk-cy
Version:
SDK to interact with the Dimension blockchain - just for learning
131 lines (119 loc) • 3.64 kB
text/typescript
import { DimensionClient, DeployUtil } from '../../src';
import { Secp256K1 } from '../../src/lib/Keys';
import {
JRPCRequest,
SendCallBack
} from '../../src/services/ProviderTransport';
const keyPair = Secp256K1.new();
function parseResponse(fetchRes: any, body: Record<string, unknown>): any {
// check for error code
if (fetchRes.status !== 200) {
throw new Error(
JSON.stringify({
message: `Non-200 status code: '${fetchRes.status}'`,
data: body
})
);
}
// check for rpc error
if (body.error) {
throw new Error(body.error as string);
}
// return successful result
return body.result;
}
const createFetchConfigFromReq = ({
req,
rpcTarget
}: {
req: JRPCRequest<unknown>;
rpcTarget: string;
}) => {
const parsedUrl: URL = new URL(rpcTarget);
// prepare payload
// copy only canonical json rpc properties
const payload = {
id: req.id,
jsonrpc: req.jsonrpc,
method: req.method,
params: req.params
};
// serialize request body
const serializedPayload: string = JSON.stringify(payload);
// configure fetch params
const fetchParams = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: serializedPayload
};
return { fetchUrl: parsedUrl.href, fetchParams };
};
const sendRpcRequestToChain = async (
req: JRPCRequest<unknown>,
rpcTarget: string
) => {
const { fetchUrl, fetchParams } = createFetchConfigFromReq({
req,
rpcTarget
});
const fetchRes = await fetch(fetchUrl, fetchParams);
if (fetchRes.status >= 400) {
throw new Error(
`Request failed with status: ${
fetchRes.status
} and body: ${JSON.stringify(fetchRes.body || {})}`
);
}
// parse response body
const fetchBody = await fetchRes.json();
const result = parseResponse(fetchRes, fetchBody as Record<string, unknown>);
// set result and exit retry loop
return result;
};
const processDeploy = async (
req: JRPCRequest<unknown>,
client: DimensionClient,
rpcTarget: string
) => {
// we can do any preprocessing or validation on deploy here,
// and then finally sign deploy and send it blockchain.
const deserializedDeploy = DeployUtil.deployFromJson(req.params as any);
if (deserializedDeploy.ok) {
const signedDeploy = client.signDeploy(deserializedDeploy.val, keyPair);
req.params = DeployUtil.deployToJson(signedDeploy);
// const jrpcResult = await sendRpcRequestToChain(req, rpcTarget);
const jrpcResult = { deploy_hash: '0x123', rpcTarget };
return jrpcResult
}
throw new Error('Failed to parse deploy');
};
export class MockProvider {
private rpcTarget: string;
private client: DimensionClient;
constructor(rpcTarget: string) {
this.rpcTarget = rpcTarget;
this.client = new DimensionClient(rpcTarget);
}
async sendAsync(req: JRPCRequest<unknown>): Promise<any> {
// we are intercepting 'account_put_deploy' (ie. signing the deploy and then submitting the signed deploy
// to blockchain)
// for rest of rpc calls we are simply sending rpc call to blockchain and returning the result.
if (req.method === 'account_put_deploy') {
return processDeploy(req, this.client, this.rpcTarget);
} else {
try {
const jrpcResult = await sendRpcRequestToChain(req, this.rpcTarget);
return jrpcResult
} catch (error) {
throw error;
}
}
}
// currently we only use sendAsync in provider transport, so we live it unimplemented here.
send(_: JRPCRequest<unknown>, __: SendCallBack<any>): void {
return;
}
}