@nomad-xyz/sdk
Version:
297 lines • 11.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NomadContext = void 0;
const multi_provider_1 = require("@nomad-xyz/multi-provider");
const config = __importStar(require("@nomad-xyz/configuration"));
const cross_fetch_1 = __importDefault(require("cross-fetch"));
const CoreContracts_1 = require("./CoreContracts");
const messageBackend_1 = require("./messageBackend");
/**
* The NomadContext manages connections to Nomad core and Bridge contracts.
* It inherits from the {@link MultiProvider}, and ensures that its contracts
* always use the latest registered providers and signers.
*
* For convenience, we've pre-constructed contexts for mainnet and testnet
* deployments. These can be imported directly.
*
* @example
* // Set up mainnet and then access contracts as below:
* let router = mainnet.mustGetBridge('celo').bridgeRouter;
*/
class NomadContext extends multi_provider_1.MultiProvider {
constructor(environment = 'development', backend) {
super();
const conf = typeof environment === 'string'
? config.getBuiltin(environment)
: environment;
config.validateConfig(conf);
this.conf = conf;
this._cores = new Map();
this._blacklist = new Set();
this._backend = backend;
for (const network of this.conf.networks) {
// register domain
this.registerDomain(this.conf.protocol.networks[network]);
// register RPC provider
if (this.conf.rpcs[network] && this.conf.rpcs[network].length > 0) {
this.registerRpcProvider(network, this.conf.rpcs[network][0]);
}
// set core contracts
const netConf = this.conf.core[network];
// type discrimination
if (!netConf.upgradeBeaconController)
throw new Error('substrate not yet supported');
const core = new CoreContracts_1.CoreContracts(this, network, netConf);
this._cores.set(core.domain, core);
}
}
/**
* Create default backend for the context
*/
withDefaultBackend() {
// TODO: What if backend doesn't exist for this environment?
this._backend = messageBackend_1.GoldSkyBackend.default(this.environment, this);
return this;
}
get governor() {
return this.conf.protocol.governor;
}
get environment() {
return this.conf.environment;
}
/**
* Register an ethers Provider for a specified domain.
*
* @param nameOrDomain A domain name or number.
* @param provider An ethers Provider to be used by requests to that domain.
*/
registerProvider(nameOrDomain, provider) {
const domain = this.resolveDomain(nameOrDomain);
super.registerProvider(domain, provider);
}
/**
* Register an ethers Signer for a specified domain.
*
* @param nameOrDomain A domain name or number.
* @param signer An ethers Signer to be used by requests to that domain.
*/
registerSigner(nameOrDomain, signer) {
const domain = this.resolveDomain(nameOrDomain);
super.registerSigner(domain, signer);
}
/**
* Remove the registered ethers Signer from a domain. This function will
* attempt to preserve any Provider that was previously connected to this
* domain.
*
* @param nameOrDomain A domain name or number.
*/
unregisterSigner(nameOrDomain) {
const domain = this.resolveDomain(nameOrDomain);
super.unregisterSigner(domain);
}
/**
* Clear all signers from all registered domains.
*/
clearSigners() {
super.clearSigners();
}
/**
* Get the {@link CoreContracts} for a given domain (or undefined)
*
* @param nameOrDomain A domain name or number.
* @returns a {@link CoreContracts} object (or undefined)
*/
getCore(nameOrDomain) {
const domain = this.resolveDomainName(nameOrDomain);
return this._cores.get(domain);
}
/**
* Get the {@link CoreContracts} for a given domain (or throw an error)
*
* @param nameOrDomain A domain name or number.
* @returns a {@link CoreContracts} object
* @throws if no {@link CoreContracts} object exists on that domain.
*/
mustGetCore(nameOrDomain) {
const core = this.getCore(nameOrDomain);
if (!core) {
throw new Error(`Missing core for domain: ${nameOrDomain}`);
}
return core;
}
/**
* Resolve the replica for the Home domain on the Remote domain (if any).
*
* WARNING: do not hold references to this contract, as it will not be
* reconnected in the event the chain connection changes.
*
* @param home the sending domain
* @param remote the receiving domain
* @returns An interface for the Replica (if any)
*/
getReplicaFor(home, remote) {
return this.getCore(remote)?.getReplica(home);
}
/**
* Resolve the replica for the Home domain on the Remote domain (or throws).
*
* WARNING: do not hold references to this contract, as it will not be
* reconnected in the event the chain connection changes.
*
* @param home the sending domain
* @param remote the receiving domain
* @returns An interface for the Replica
* @throws If no replica is found.
*/
mustGetReplicaFor(home, remote) {
const replica = this.getReplicaFor(home, remote);
if (!replica) {
throw new Error(`Missing replica for home ${home} & remote ${remote}`);
}
return replica;
}
/**
* Discovers the governor domain of this nomad deployment and returns the
* associated Core.
*
* @returns The identifier of the governing domain
*/
governorCore() {
return this.mustGetCore(this.governor.domain);
}
/**
* Proves and Processes a transaction on the destination chain. This is subsidize and
* automatic on non-Ethereum destinations
*
* @dev Ensure that a transaction is ready to be processed. You should ensure the following
* criteria have been met prior to calling this function:
* 1. The tx has been relayed (has status of 2):
* `const status = await NomadMessage.status()`
* 2. The `confirmAt` timestamp for the tx is in the past:
* `const confirmAt = await NomadMessage.confirmAt()`
*
* @param message NomadMessage
* @param overrides Any tx overrides (e.g. gas limit, gas price)
* @returns The Contract Transaction receipt
*/
async process(message, overrides = {}) {
const data = await message.getProof();
if (!data)
throw new Error('Unable to fetch proof');
return this.processProof(message.origin, message.destination, data, overrides);
}
async processProof(origin, destination, proof, overrides = {}) {
// get replica contract
const replica = this.mustGetReplicaFor(origin, destination);
await replica.callStatic.proveAndProcess(proof.message, proof.proof.path, proof.proof.index, overrides);
return replica.proveAndProcess(proof.message, proof.proof.path, proof.proof.index, overrides);
}
async fetchProof(origin, leafIndex) {
const s3 = this.conf.s3;
if (!s3)
throw new Error('s3 data not configured');
const { bucket, region } = s3;
const originName = this.resolveDomainName(origin);
const uri = `https://${bucket}.s3.${region}.amazonaws.com/${originName}_${leafIndex}`;
const response = await (0, cross_fetch_1.default)(uri);
if (!response)
throw new Error('Unable to fetch proof');
const data = await response.json();
if (data.proof && data.message)
return data;
throw new Error('Server returned invalid proof');
}
async processByOriginDestinationAndLeaf(origin, destination, leafIndex) {
const proof = await this.fetchProof(origin, leafIndex);
return await this.processProof(origin, destination, proof);
}
blacklist() {
return this._blacklist;
}
async checkHomes(networks) {
for (const n of networks) {
await this.checkHome(n);
}
}
async checkHome(nameOrDomain) {
const domain = this.resolveDomain(nameOrDomain);
const home = this.mustGetCore(domain).home;
const state = await home.state();
if (state === 2) {
console.log(`Home for domain ${domain} is failed!`);
this._blacklist.add(domain);
}
else {
this._blacklist.delete(domain);
}
}
/**
* Fetch a config from the Nomad config static site.
*
* @param environment the environment name to attempt to fetch
* @returns A NomadConfig
* @throws If the site is down, the config is not on the site, or the config
* is not of a valid format
*/
static async fetchConfig(environment) {
const uri = `https://nomad-xyz.github.io/config/${environment}.json`;
const config = await (await (0, cross_fetch_1.default)(uri, { cache: 'no-cache' })).json();
return config;
}
/**
* Fetch a config from the Nomad config static site and instantiate a context
* from it. If there is an issue, this function will fallback to the latest
* version of the config shipped with the configuration package.
*
* Fallback may be disabled by setting `allowFallback` to false
*
* @param this this type for the descendant
* @param env the environment name to attempt to fetch
* @param allowFallback allow fallback to the builtin env configuration
* @returns A NomadContext with the latest configuration for the specified env
* @throws If `allowFallback` is false and the site is down, the config is
* not on the site, or the config is not of a valid format
*/
static async fetch(env, allowFallback = true) {
try {
const config = await NomadContext.fetchConfig(env);
return new this(config);
}
catch (e) {
if (allowFallback) {
console.warn(`Unable to retrieve config ${env}. Falling back to built-in config.\n${e}`);
return new this(env);
}
throw e;
}
}
}
exports.NomadContext = NomadContext;
//# sourceMappingURL=NomadContext.js.map