@nomad-xyz/sdk
Version:
380 lines • 12.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoldSkyBackend = void 0;
const ethers_1 = require("ethers");
const graphql_request_1 = require("graphql-request");
const backend_1 = require("./backend");
const __1 = require("..");
const utils_1 = require("./utils");
const defaultGoldSkySecret = "mpa%H&RAHu9;eUe";
const supportedEnvironments = [
"production",
"development",
];
/**
* GoldSky backend for NomadMessage
*/
class GoldSkyBackend extends backend_1.MessageBackend {
constructor(env, secret, context) {
super();
this.env = env;
this._secret = secret;
this.messageCache = new Map();
this.dispatchTxToMessageHash = new Map();
this.context = context;
}
/**
* Resolves current context used with the backend
* @returns Nomad context
*/
getContext() {
return this.context;
}
/**
* Checks whether the backend environment is supported
* @param environment name of the environment as string
*/
static checkEnvironment(environment) {
if (!supportedEnvironments.includes(environment)) {
throw new Error(`Only the following environments are supported: ${supportedEnvironments.join(', ')}. Provided: ${environment}`);
}
}
/**
* Returns default secret for Goldsky
* @returns secret as a string
*/
static defaultSecret() {
return defaultGoldSkySecret;
}
/**
* Default GoldSky backend for the environment
* @param environment name of the environment as string
* @param context Nomad context to be used with the backend
* @returns
*/
static default(environment = 'development', context) {
const environmentString = typeof environment === 'string' ? environment : environment.environment;
GoldSkyBackend.checkEnvironment(environmentString);
const secret = process.env.GOLDSKY_SECRET || GoldSkyBackend.defaultSecret();
if (!secret)
throw new Error(`GOLDSKY_SECRET not found in env`);
return new GoldSkyBackend(environmentString, secret, context || new __1.NomadContext(environment));
}
/**
* Fills filter with default values required for fetching a message from backend
*
* @returns Filled event filter
*/
static fillFilter(f) {
return {
committedRoot: f.committedRoot || '',
messageHash: f.messageHash || '',
transactionHash: f.transactionHash || '',
};
}
/**
* Prepares a URI that is used for fetching messages
*
* @returns uri
*/
get uri() {
// return `https://${this.env}.goldsky.io/c/nomad/gql/v1/graphql`
return `https://api.goldsky.io/c/nomad/gql/v1/graphql`;
}
/**
* Prepares headers for connecting to hasura
*/
get headers() {
return {
'content-type': 'application/json',
'x-goldsky-secret': this._secret,
};
}
/**
* Prepares Dispatch events from backend's internal message representation
*
* @returns Dispatch events assiciated with transaction (if any)
*/
async getDispatches(tx, limit) {
const ms = await this.getMessagesByTx(tx, limit);
if (!ms)
return undefined;
return ms.map((m) => ({
args: {
messageHash: m.message_hash,
leafIndex: ethers_1.BigNumber.from(m.leaf_index),
destinationAndNonce: ethers_1.BigNumber.from(m.destination_and_nonce),
committedRoot: m.committed_root,
message: m.message,
},
transactionHash: m.dispatch_tx,
}));
}
/**
* Prepares a Dispatch event from backend's internal message representation
*
* @returns Dispatch event assiciated with message hash (if any)
*/
async getDispatchByMessageHash(messageHash) {
const ms = await this.getMessage(messageHash);
if (!ms)
return;
return ({
args: {
messageHash: ms?.message_hash,
leafIndex: ethers_1.BigNumber.from(ms.leaf_index),
destinationAndNonce: ethers_1.BigNumber.from(ms.destination_and_nonce),
committedRoot: ms.committed_root,
message: ms.message,
},
transactionHash: ms.dispatch_tx,
});
}
/**
* Stores message into internal cache
*/
storeMessage(m) {
this.messageCache.set(m.message_hash, m);
const messageHashes = this.dispatchTxToMessageHash.get(m.dispatch_tx);
if (!messageHashes) {
this.dispatchTxToMessageHash.set(m.dispatch_tx, [m.message_hash]);
}
else {
if (!messageHashes.includes(m.message_hash))
messageHashes.push(m.message_hash);
}
}
/**
* Get the message representation associated with this message (if any)
* by message hash
*
* @returns A message representation (if any)
*/
async getMessage(messageHash, forceFetch = false) {
let m = this.messageCache.get(messageHash);
if (!m || forceFetch) {
m = (await this.fetchMessages({
messageHash,
}, 1))?.[0];
if (m) {
this.storeMessage(m);
}
}
return m;
}
/**
* Get the message representation associated with this message (if any)
* by dispatch transaction
*
* @returns A message representation (if any)
*/
async getMessagesByTx(tx, limit, forceFetch = true) {
let ms;
const messageHashes = this.dispatchTxToMessageHash.get(tx);
const enoughMessages = limit && messageHashes && limit <= messageHashes.length;
if (!enoughMessages || forceFetch) {
ms = await this.fetchMessages({
transactionHash: tx,
});
if (ms && ms.length) {
ms.forEach((m) => this.storeMessage(m));
}
}
else {
if (!messageHashes)
throw new Error('MessageHashes are unexpectedly not existing');
ms = await Promise.all(messageHashes.map(async (hash) => {
const message = await this.getMessage(hash);
if (!message)
throw new Error("Couldn't get a message from existing messages."); // Message must be in messageHashes
return message;
}));
}
return ms;
}
/**
* Get the `Dispatch` transaction hash associated with this message (if any)
*
* @returns A dispatch tx (if any)
*/
async dispatchTx(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.dispatch_tx)
m = await this.getMessage(messageHash, true);
return m?.dispatch_tx;
}
/**
* Get the `Relay` transaction hash associated with this message (if any)
*
* @returns A relay tx (if any)
*/
async relayTx(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.relay_tx)
m = await this.getMessage(messageHash, true);
return m?.relay_tx;
}
/**
* Get the `Update` transaction hash associated with this message (if any)
*
* @returns A update tx (if any)
*/
async updateTx(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.update_tx)
m = await this.getMessage(messageHash, true);
return m?.update_tx;
}
/**
* Get the `Process` transaction hash associated with this message (if any)
*
* @returns A relay tx (if any)
*/
async processTx(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.process_tx)
m = await this.getMessage(messageHash, true);
return m?.process_tx;
}
/**
* Get the `Dispatch` transaction hash associated with this message (if any)
*
* @returns A dispatch tx (if any)
*/
async dispatchedAt(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.dispatched_at)
m = await this.getMessage(messageHash, true);
return m ? new Date(m.dispatched_at) : undefined;
}
/**
* Get the `Relay` transaction hash associated with this message (if any)
*
* @returns A relay tx (if any)
*/
async relayedAt(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.relayed_at)
m = await this.getMessage(messageHash, true);
return m && m.relayed_at ? new Date(m.relayed_at) : undefined;
}
/**
* Get the `Update` transaction hash associated with this message (if any)
*
* @returns A update tx (if any)
*/
async updatedAt(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.updated_at)
m = await this.getMessage(messageHash, true);
return m && m.updated_at ? new Date(m.updated_at) : undefined;
}
/**
* Get the `Process` transaction hash associated with this message (if any)
*
* @returns A relay tx (if any)
*/
async processedAt(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.processed_at)
m = await this.getMessage(messageHash, true);
return m && m.processed_at ? new Date(m.processed_at) : undefined;
}
/**
* Gets destination domain for a specific message
* @param messageHash message hash identifier
* @returns destination domain id
*/
async destinationDomainId(messageHash) {
let m = await this.getMessage(messageHash);
if (!m?.destination_domain_id)
m = await this.getMessage(messageHash, true);
return m?.destination_domain_id;
}
/**
* Get message hash associated with this message (if any)
*
* @returns A message hash (if any)
*/
async getFirstMessageHash(tx) {
const ms = await this.getMessagesByTx(tx);
return ms?.[0]?.message_hash;
}
/**
* Fetches internal message from backend
*
* @returns Internal message representation (if any)
*/
async fetchMessages(f, limit) {
const eventsTable = `${this.env}_views_events`;
const query = (0, graphql_request_1.gql) `
query Query(
$committedRoot: String
$messageHash: String
$transactionHash: String
$limit: Int
) {
${eventsTable}(
where: {
_or: [
{ dispatch_tx: { _eq: $transactionHash } }
{ message_hash: { _eq: $messageHash } }
{ old_root: { _eq: $committedRoot } }
]
}
limit: $limit
) {
committed_root
destination_and_nonce
destination_domain_id
destination_domain_name
dispatch_block
dispatch_tx
dispatched_at
id
leaf_index
message
message__action__amount
message__action__details_hash
message__action__to
message__action__type
message__token__domain
message__token__id
message_body
message_hash
message_type
new_root
nonce
old_root
origin_domain_id
origin_domain_name
process_block
process_tx
processed_at
recipient_address
relay_block
relay_chain_id
relay_tx
relayed_at
sender_address
signature
update_block
update_chain_id
update_tx
updated_at
}
}
`;
const filter = {
...GoldSkyBackend.fillFilter(f),
limit: limit || null,
};
const response = await (0, graphql_request_1.request)(this.uri, query, filter, this.headers);
const events = (0, utils_1.nulls2undefined)(response[eventsTable]);
if (!events || events.length <= 0)
return undefined;
return events;
}
}
exports.GoldSkyBackend = GoldSkyBackend;
//# sourceMappingURL=goldsky.js.map