ramda#map JavaScript Examples

The following examples show how to use ramda#map. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: requests.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export async function fetchRequestsByChainId({ chainId }) {
  const data = await client
    .query({
      TableName: requestsTable,
      IndexName: "byChainIdAndStatus",
      ...buildKeyConditionExpression({ chainId }),
    })
    .promise();

  return map(denormalizeData, data.Items);
}
Example #2
Source File: fhir-references.js    From discovery-mobile-ui with MIT License 6 votes vote down vote up
referenceMap = {
  // each key is a "type" (but not the referenced _resource_ type, eg: "Practitioner")
  // each value operates on a FHIR resource, and returns an Array of reference Objects
  serviceProvider: compose(
    (result) => (result ? [result] : []),
    path(['serviceProvider']),
  ),
  requester: compose(
    (result) => (result ? [result] : []),
    path(['requester']), // may or may not be practitioner?
  ),
  participant: compose(
    map(path(['individual'])), // may or may not be practitioner?
    filter(hasPath(['individual', 'reference'])),
    propOr([], 'participant'),
  ),
}
Example #3
Source File: NotificationStack.js    From lundium with MIT License 6 votes vote down vote up
NotificationStack = ({
	notifications,
	renderNotification,
	transition = defaultTransition,
	className,
	...otherProps
}) => (
	<Portal container={getParentContainer()} {...otherProps}>
		<TransitionGroup className={cx('notification-stack', className)}>
			{map(
				notification => (
					<CSSTransition key={`transition-${notification.id}`} {...transition}>
						<div>
							{renderNotification({
								...notification,
								key: `notification-${notification.id}`,
							})}
						</div>
					</CSSTransition>
				),
				notifications,
			)}
		</TransitionGroup>
	</Portal>
)
Example #4
Source File: Box.js    From lundium with MIT License 6 votes vote down vote up
Box = forwardRef(
	(
		{
			children,
			as: Comp = 'div',
			className,
			display,
			hasStrictAttributes,
			elementProps,
			...rest
		},
		ref,
	) => (
		<Comp
			className={cx(
				...map(x => propToClassName(x)(rest[x]))(DIMENSIONS),
				displayToClassName(display),
				className,
			)}
			ref={ref}
			{...(hasStrictAttributes
				? omitInvalidProps(rest)
				: omit(DIMENSIONS, rest))}
			{...elementProps}
		>
			{children}
		</Comp>
	),
)
Example #5
Source File: index.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
buildGenericConditionExpression = curry(function _buildGenericConditionExpression(exprAttr, obj) {
  const buildDescriptors = compose(
    map(([key, value]) => ({
      expr: `#${key} = :${key}`,
      name: { [`#${key}`]: key },
      value: { [`:${key}`]: value },
    })),
    toPairs
  );
  const descriptors = buildDescriptors(obj);

  const buildExpression = compose(join(" and "), pluck("expr"));
  const buildAttrNames = compose(mergeAll, pluck("name"));
  const buildAttrValues = compose(mergeAll, pluck("value"));

  const expression = buildExpression(descriptors);
  const attrNames = buildAttrNames(descriptors);
  const attrValues = buildAttrValues(descriptors);

  return {
    [exprAttr]: expression,
    ExpressionAttributeNames: attrNames,
    ExpressionAttributeValues: attrValues,
  };
})
Example #6
Source File: index.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export function buildSetUpdateExpression(obj) {
  const buildDescriptors = compose(
    map(([key, value]) => ({
      expr: `#${key} = :${key}`,
      name: { [`#${key}`]: key },
      value: { [`:${key}`]: value },
    })),
    toPairs
  );
  const descriptors = buildDescriptors(obj);

  const buildExpression = compose((expr) => `SET ${expr}`, join(", "), pluck("expr"));
  const buildAttrNames = compose(mergeAll, pluck("name"));
  const buildAttrValues = compose(mergeAll, pluck("value"));

  const expression = buildExpression(descriptors);
  const attrNames = buildAttrNames(descriptors);
  const attrValues = buildAttrValues(descriptors);

  return {
    UpdateExpression: expression,
    ExpressionAttributeNames: attrNames,
    ExpressionAttributeValues: attrValues,
  };
}
Example #7
Source File: index.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export function createEnhancedClient() {
  const params = process.env.IS_LOCAL
    ? {
        region: "localhost",
        endpoint: "http://localhost:8000",
      }
    : {};

  const client = new DynamoDB.DocumentClient(params);

  const writeSingleBatch = curry(function _writeSingleBatch(tableName, batch) {
    return client
      .batchWrite({
        RequestItems: {
          [tableName]: batch,
        },
      })
      .promise();
  });

  const writeAllBatches = function _writeAllBatches(tableName, items) {
    return compose(map(writeSingleBatch(tableName)), splitBatches)(items);
  };

  const batchWrite = curry(async function _batchWrite(tableName, items) {
    return flatten(await P.all(writeAllBatches(tableName, items)));
  });

  return {
    client,
    batchWrite,
  };
}
Example #8
Source File: requests.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export async function fetchRequestsByChainIdAndStatus({ chainId, status }) {
  const data = await client
    .query({
      TableName: requestsTable,
      IndexName: "byChainIdAndStatus",
      ...buildKeyConditionExpression({ chainId, status }),
    })
    .promise();

  return map(denormalizeData, data.Items);
}
Example #9
Source File: requests.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export async function deleteAllRequests() {
  const createDeleteRequest = (item) => ({
    DeleteRequest: {
      Key: item,
    },
  });

  const data = await fetchAllRequestIds();

  if (data.length === 0) {
    return;
  }

  return compose(
    batchWrite(requestsTable),
    map(compose(createDeleteRequest, pick(["requestId", "chainId"], normalizeData)))
  )(data);
}
Example #10
Source File: requests.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export async function fetchAllRequestIds() {
  const data = await client
    .scan({
      TableName: requestsTable,
      ProjectionExpression: "requestId, chainId",
    })
    .promise();

  return map(denormalizeData, data.Items);
}
Example #11
Source File: requests.js    From cross-chain-realitio-proxy with MIT License 6 votes vote down vote up
export async function saveRequests(requests) {
  const createPutRequest = (item) => ({
    PutRequest: {
      Item: item,
    },
  });

  const createBatchItem = compose(createPutRequest, extractStoredData, normalizeData);

  return compose(batchWrite(requestsTable), map(createBatchItem))(requests);
}
Example #12
Source File: staking_payouts.js    From sdk with MIT License 6 votes vote down vote up
fetchErasInfo = async (api) => {
  const eras = await api.derive.staking.erasHistoric();
  const indexByEra = indexBy(o((era) => era.toString(), prop("era")));

  const [pointsByEra, rewardsByEra, prefsByEra] = await Promise.all([
    api.derive.staking._erasPoints(eras),
    api.derive.staking._erasRewards(eras),
    api.derive.staking._erasPrefs(eras),
  ]).then(map(indexByEra));

  return {
    eras,
    pointsByEra,
    rewardsByEra,
    prefsByEra,
  };
}
Example #13
Source File: createApiInstance.js    From cross-chain-realitio-proxy with MIT License 5 votes vote down vote up
export default async function createApiInstance() {
  const [batchSend, foreignProxy] = await Promise.all([
    createBatchSend(web3, FOREIGN_TX_BATCHER_CONTRACT_ADDRESS),
    getContract(web3, ForeignProxy.abi, process.env.FOREIGN_PROXY_CONTRACT_ADDRESS),
  ]);

  async function getBlockNumber() {
    return Number(await web3.eth.getBlockNumber());
  }

  async function getChainId() {
    return Number(await web3.eth.getChainId());
  }

  async function getArbitrationRequest({ questionId, requester }) {
    const [arbitration, chainId] = await P.all([
      foreignProxy.methods.arbitrationRequests(questionId, requester).call(),
      getChainId(),
    ]);

    return {
      ...arbitration,
      chainId,
      questionId,
      requester,
      status: Number(arbitration.status),
    };
  }

  async function getRequestedArbitrations({ fromBlock = 0, toBlock = "latest" } = {}) {
    const events = await getPastEvents(foreignProxy, "ArbitrationRequested", { fromBlock, toBlock });

    const allNotifiedRequests = await P.allSettled(
      map(
        ({ returnValues }) =>
          getArbitrationRequest({
            questionId: returnValues._questionID,
            requester: returnValues._requester,
          }),
        events
      )
    );
    const onlyFulfilled = compose(filter(propEq("status", "fulfilled")), map(prop("value")));

    return into([], onlyFulfilled, allNotifiedRequests);
  }

  async function handleFailedDisputeCreation(arbitration) {
    await batchSend({
      args: [arbitration.questionId, arbitration.requester],
      method: foreignProxy.methods.handleFailedDisputeCreation,
      to: foreignProxy.options.address,
    });
    return arbitration;
  }

  return {
    getChainId,
    getBlockNumber,
    getArbitrationRequest,
    getRequestedArbitrations,
    handleFailedDisputeCreation,
  };
}
Example #14
Source File: Grid.js    From lundium with MIT License 5 votes vote down vote up
convertToDisplay = (property, defaultProperty, cssResponsiveValue) => {
	const getProperty = sanitizeCssValue(property)(defaultProperty);

	return cssResponsiveValue &&
		(isArray(cssResponsiveValue) || isPlainObject(cssResponsiveValue))
		? map(getProperty, cssResponsiveValue)
		: getProperty(cssResponsiveValue);
}
Example #15
Source File: staking_payouts.js    From sdk with MIT License 4 votes vote down vote up
/**
 * Sends staking payouts for recent eras using the given sender account.
 *
 * @param {DockAPI} dock
 * @param {ErasInfo} erasInfo
 * @param {*} initiator - account to send tx from
 * @param {number} batchSize
 * @returns {Promise<boolean>} - `true` if some unpaid eras were found, `false` otherwise
 */
async function sendStakingPayouts(dock, erasInfo, initiator, batchSize) {
  const validators = pipe(
    values,
    pluck("validators"),
    chain(keys),
    (values) => new Set(values)
  )(erasInfo.pointsByEra);

  if (!validators.size) {
    console.log("- Validator set is empty.");
    return false;
  }

  console.log("- Retrieving validator eras...");
  const erasToBePaid = await lastValueFrom(
    from(validators).pipe(
      getUnclaimedStashesEras(dock.api, erasInfo),
      toArray()
    )
  );

  const rewards = pipe(buildValidatorRewards, toPairs)(erasInfo, erasToBePaid);

  if (isEmpty(rewards)) {
    console.log("- No unpaid validator rewards found for recent eras.");
    return false;
  }

  console.log("- Payouts need to be made:");

  for (const [stashId, eras] of rewards) {
    const total = eras.reduce(
      (total, { reward }) => (total != null ? total.add(reward) : reward),
      null
    );

    console.log(` * To \`${stashId}\`: ${formatDock(total)}`);
  }

  const logResult = ({ result, tx }) => {
    // Ther's a much better way to check this...
    const isBatch = tx.method.args?.[0]?.[0] instanceof Map;

    let msg = " * ";
    if (isBatch) msg += `Batch transaction`;
    else msg += `Transaction`;

    if (FinalizeTx) {
      msg += ` finalized at block \`${result.status.asFinalized}\`: `;
    } else {
      msg += ` included at block \`${result.status.asInBlock}\`: `;
    }

    const payoutsSummary = (isBatch ? tx.method.args[0] : [tx.method])
      .map((payout) => payout.get("args"))
      .map(
        ({ validator_stash, era }) =>
          `\`${validator_stash}\` rewarded for era ${era}`
      )
      .join(isBatch ? ",\n    " : ",");

    msg += isBatch ? `[\n    ${payoutsSummary}\n ]` : payoutsSummary;

    console.log(msg);
  };

  const payoutTxs$ = from(rewards).pipe(
    concatMap(([stashId, eras]) => from(eras.map(assoc("stashId", stashId)))),
    mapRx(({ stashId, era }) =>
      dock.api.tx.staking.payoutStakers(stashId, era)
    ),
    batchExtrinsics(dock.api, batchSize),
    signAndSendExtrinsics(dock, initiator),
    tap(logResult)
  );

  await new Promise((resolve, reject) =>
    payoutTxs$.subscribe({
      error: reject,
      complete: resolve,
    })
  );

  return true;
}
Example #16
Source File: checkRequestedArbitrations.js    From cross-chain-realitio-proxy with MIT License 4 votes vote down vote up
export default async function checkRequestedArbitrations({ foreignChainApi }) {
  const chainId = await foreignChainApi.getChainId();

  await checkUntrackedArbitrationRequests();
  await processArbitrationRequests();

  async function checkUntrackedArbitrationRequests() {
    const [fromBlock, toBlock] = await P.all([
      getBlockHeight("ACCEPTED_ARBITRATION_REQUESTS"),
      foreignChainApi.getBlockNumber(),
    ]);

    const untrackedArbitrationRequests = filter(
      ({ status }) => status !== status.None,
      await foreignChainApi.getRequestedArbitrations({ fromBlock, toBlock })
    );

    await saveRequests(untrackedArbitrationRequests);

    const blockHeight = toBlock + 1;
    await updateBlockHeight({ key: "ACCEPTED_ARBITRATION_REQUESTS", blockHeight });
    console.info({ blockHeight }, "Set ACCEPTED_ARBITRATION_REQUESTS block height");

    const stats = {
      data: map(pick(["questionId", "requester", "chainId", "status"]), untrackedArbitrationRequests),
      fromBlock,
      toBlock,
    };

    console.info(stats, "Found new notified arbitration requests");

    return stats;
  }

  async function processArbitrationRequests() {
    const requestRemovedOrRuled = ([_, onChainArbitration]) =>
      [Status.None, Status.Ruled].includes(onChainArbitration.status);
    const removeOffChainRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      removeRequest,
      (arbitration) => ({
        action: "ARBITRATION_REQUEST_REMOVED",
        payload: arbitration,
      }),
    ]);

    const disputeCreationFailed = ([_, onChainArbitration]) => onChainArbitration.status === Status.Failed;
    const handleFailedDisputeCreation = asyncPipe([
      mergeOnChainOntoOffChain,
      foreignChainApi.handleFailedDisputeCreation,
      (arbitration) => ({
        action: "FAILED_DISPUTE_CREATION_HANDLED",
        payload: arbitration,
      }),
    ]);

    const requestStatusChanged = ([offChainArbitration, onChainArbitration]) =>
      offChainArbitration.status != onChainArbitration.status;
    const updateOffChainRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      updateRequest,
      (arbitration) => ({
        action: "STATUS_CHANGED",
        payload: arbitration,
      }),
    ]);

    const noop = asyncPipe([
      mergeOnChainOntoOffChain,
      (arbtration) => ({
        action: "NO_OP",
        payload: arbtration,
      }),
    ]);

    const pipeline = asyncPipe([
      fetchOnChainCounterpart,
      cond([
        [requestRemovedOrRuled, removeOffChainRequest],
        [disputeCreationFailed, handleFailedDisputeCreation],
        [requestStatusChanged, updateOffChainRequest],
        [() => true, noop],
      ]),
    ]);

    const requestedArbitrations = await fetchRequestsByChainId({ chainId });

    console.info(
      { data: map(pick(["questionId", "requester"]), requestedArbitrations) },
      "Fetched requested arbitrations"
    );

    const results = await P.allSettled(map(pipeline, requestedArbitrations));

    const groupQuestionsOrErrorMessage = (acc, r) => {
      if (r.status === "rejected") {
        return [...acc, r.reason?.message];
      }

      const questionId = r.value?.payload?.questionId ?? "<not set>";
      const requester = r.value?.payload?.requester ?? "<not set>";
      return [...acc, { questionId, requester }];
    };
    const toTag = (r) => (r.status === "rejected" ? "FAILURE" : r.value?.action);
    const stats = reduceBy(groupQuestionsOrErrorMessage, [], toTag, results);

    console.info(stats, "Processed requested arbitrations");

    return stats;
  }

  async function fetchOnChainCounterpart(offChainArbitration) {
    const { questionId, requester } = offChainArbitration;

    const onChainArbitration = await foreignChainApi.getArbitrationRequest({ questionId, requester });

    return [offChainArbitration, onChainArbitration];
  }
}
Example #17
Source File: checkRejectedRequests.js    From cross-chain-realitio-proxy with MIT License 4 votes vote down vote up
export default async function checkRejectedRequests({ homeChainApi }) {
  const chainId = await homeChainApi.getChainId();

  await checkUntrackedRejectedRequests();
  await checkCurrentRejectedRequests();

  async function checkUntrackedRejectedRequests() {
    const [fromBlock, toBlock] = await Promise.all([
      getBlockHeight("REJECTED_REQUESTS"),
      homeChainApi.getBlockNumber(),
    ]);

    const untrackedRejectedRequests = filter(
      ({ status }) => status === Status.Rejected,
      await homeChainApi.getRejectedRequests({ fromBlock, toBlock })
    );

    await saveRequests(untrackedRejectedRequests);

    const blockHeight = toBlock + 1;
    await updateBlockHeight({ key: "REJECTED_REQUESTS", blockHeight });
    console.info({ blockHeight }, "Set REJECTED_REQUESTS block height");

    const stats = {
      data: map(pick(["questionId", "requester"]), untrackedRejectedRequests),
      fromBlock,
      toBlock,
    };

    console.info(stats, "Found new rejected arbitration requests");

    return stats;
  }

  async function checkCurrentRejectedRequests() {
    const requestRemoved = ([_, onChainArbitration]) => onChainArbitration.status === Status.None;
    const removeOffChainRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      removeRequest,
      (arbitration) => ({
        action: "REQUEST_REMOVED",
        payload: arbitration,
      }),
    ]);

    const requestRejected = ([_, onChainArbitration]) => onChainArbitration.status === Status.Rejected;
    const handleRejectedRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      homeChainApi.handleRejectedRequest,
      (request) => ({
        action: "REJECTED_REQUEST_HANDLED",
        payload: request,
      }),
    ]);

    const noop = asyncPipe([
      mergeOnChainOntoOffChain,
      (request) => ({
        action: "NO_OP",
        payload: request,
      }),
    ]);

    const pipeline = asyncPipe([
      fetchOnChainCounterpart,
      cond([
        [requestRemoved, removeOffChainRequest],
        [requestRejected, handleRejectedRequest],
        [() => true, noop],
      ]),
    ]);

    const rejectedRequests = await fetchRequestsByChainIdAndStatus({ chainId, status: Status.Rejected });

    console.info({ data: map(pick(["questionId", "requester"]), rejectedRequests) }, "Fetched rejected requests");

    const results = await P.allSettled(map(pipeline, rejectedRequests));

    const groupQuestionsOrErrorMessage = (acc, r) => {
      if (r.status === "rejected") {
        return [...acc, r.reason?.message];
      }

      const questionId = r.value?.payload?.questionId ?? "<not set>";
      const requester = r.value?.payload?.requester ?? "<not set>";
      return [...acc, { questionId, requester }];
    };
    const toTag = (r) => (r.status === "rejected" ? "FAILURE" : r.value?.action);
    const stats = reduceBy(groupQuestionsOrErrorMessage, [], toTag, results);

    console.info(stats, "Processed rejected requests");

    return stats;
  }

  async function fetchOnChainCounterpart(offChainRequest) {
    const { questionId, requester } = offChainRequest;

    const onChainRequest = await homeChainApi.getRequest({ questionId, requester });

    return [offChainRequest, onChainRequest];
  }
}
Example #18
Source File: createApiInstance.js    From cross-chain-realitio-proxy with MIT License 4 votes vote down vote up
export default async function createApiInstance() {
  const [batchSend, homeProxy, realitio] = await Promise.all([
    createBatchSend(web3, HOME_TX_BATCHER_CONTRACT_ADDRESS),
    getContract(web3, HomeProxy.abi, process.env.HOME_PROXY_CONTRACT_ADDRESS),
    getContract(web3, RealitioInterface.abi, process.env.HOME_REALITIO_CONTRACT_ADDRESS),
  ]);

  async function getBlockNumber() {
    return Number(await web3.eth.getBlockNumber());
  }

  async function getChainId() {
    return Number(await web3.eth.getChainId());
  }

  async function getRequest({ questionId, requester }) {
    const [request, chainId] = await Promise.all([
      homeProxy.methods.requests(questionId, requester).call(),
      getChainId(),
    ]);

    return {
      ...request,
      chainId,
      questionId,
      requester,
      status: Number(request.status),
    };
  }

  async function getNotifiedRequests({ fromBlock = 0, toBlock = "latest" } = {}) {
    const events = await getPastEvents(homeProxy, "RequestNotified", { fromBlock, toBlock });

    const allNotifiedRequests = await P.allSettled(
      map(
        ({ returnValues }) =>
          getRequest({
            questionId: returnValues._questionID,
            requester: returnValues._requester,
          }),
        events
      )
    );

    const onlyFulfilled = compose(filter(propEq("status", "fulfilled")), map(prop("value")));

    return into([], onlyFulfilled, allNotifiedRequests);
  }

  async function getRejectedRequests({ fromBlock = 0, toBlock = "latest" } = {}) {
    const events = await getPastEvents(homeProxy, "RequestRejected", { fromBlock, toBlock });

    const allRejectedRequests = await P.allSettled(
      map(
        ({ returnValues }) =>
          getRequest({
            questionId: returnValues._questionID,
            requester: returnValues._requester,
          }),
        events
      )
    );
    const onlyFulfilled = compose(filter(propEq("status", "fulfilled")), map(prop("value")));

    return into([], onlyFulfilled, allRejectedRequests);
  }

  async function handleNotifiedRequest(request) {
    await batchSend({
      args: [request.questionId, request.requester],
      method: homeProxy.methods.handleNotifiedRequest,
      to: homeProxy.options.address,
    });
    return request;
  }

  async function handleChangedAnswer(request) {
    await batchSend({
      args: [request.questionId, request.requester],
      method: homeProxy.methods.handleChangedAnswer,
      to: homeProxy.options.address,
    });
    return request;
  }

  async function handleFinalizedQuestion(request) {
    await batchSend({
      args: [request.questionId, request.requester],
      method: homeProxy.methods.handleFinalizedQuestion,
      to: homeProxy.options.address,
    });
    return request;
  }

  async function handleRejectedRequest(request) {
    await batchSend({
      args: [request.questionId, request.requester],
      method: homeProxy.methods.handleRejectedRequest,
      to: homeProxy.options.address,
    });
    return request;
  }

  async function reportArbitrationAnswer(request) {
    const { questionId } = request;
    const { historyHash, answerOrCommitmentID, answerer } = await _getLatestAnswerParams(questionId);

    await batchSend({
      args: [questionId, historyHash, answerOrCommitmentID, answerer],
      method: homeProxy.methods.reportArbitrationAnswer,
      to: homeProxy.options.address,
    });
    return request;
  }

  async function _getLatestAnswerParams(questionId) {
    const answers = await getPastEvents(realitio, "LogNewAnswer", {
      filter: {
        question_id: questionId,
      },
    });

    if (answers.length == 0) {
      throw new Error(`Question ${questionId} was never answered`);
    }

    const byMostRecentBlock = descend(prop("blockNumber"));
    const sortedAnswers = sort(byMostRecentBlock, answers);

    const latestAnswer = sortedAnswers[0].returnValues;
    const previousAnswer = sortedAnswers[1]?.returnValues;

    return {
      historyHash: previousAnswer?.history_hash ?? ZERO_HASH,
      answerOrCommitmentID: latestAnswer.answer,
      answerer: latestAnswer.user,
    };
  }

  return {
    getBlockNumber,
    getChainId,
    getNotifiedRequests,
    getRejectedRequests,
    getRequest,
    handleChangedAnswer,
    handleFinalizedQuestion,
    handleNotifiedRequest,
    handleRejectedRequest,
    reportArbitrationAnswer,
  };
}
Example #19
Source File: checkNotifiedRequests.js    From cross-chain-realitio-proxy with MIT License 4 votes vote down vote up
export default async function checkNotifiedRequests({ homeChainApi }) {
  const chainId = await homeChainApi.getChainId();

  await checkUntrackedNotifiedRequests();
  await checkCurrentNotifiedRequests();

  async function checkUntrackedNotifiedRequests() {
    const [fromBlock, toBlock] = await P.all([getBlockHeight("NOTIFIED_REQUESTS"), homeChainApi.getBlockNumber()]);

    const untrackedNotifiedRequests = filter(
      ({ status }) => status !== Status.None,
      await homeChainApi.getNotifiedRequests({ fromBlock, toBlock })
    );

    await saveRequests(untrackedNotifiedRequests);

    const blockHeight = toBlock + 1;
    await updateBlockHeight({ key: "NOTIFIED_REQUESTS", blockHeight });
    console.info({ blockHeight }, "Set NOTIFIED_REQUESTS block height");

    const stats = {
      data: map(pick(["questionId", "chainId", "status"]), untrackedNotifiedRequests),
      fromBlock,
      toBlock,
    };

    console.info(stats, "Found new notified arbitration requests");

    return stats;
  }

  async function checkCurrentNotifiedRequests() {
    const requestNotified = ([_, onChainArbitration]) => onChainArbitration.status === Status.Notified;
    const handleNotifiedRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      homeChainApi.handleNotifiedRequest,
      (request) => ({
        action: "NOTIFIED_REQUEST_HANDLED",
        payload: request,
      }),
    ]);

    const requestStatusChanged = ([offChainRequest, onChainRequest]) => offChainRequest.status != onChainRequest.status;
    const updateOffChainRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      updateRequest,
      (request) => ({
        action: "STATUS_CHANGED",
        payload: request,
      }),
    ]);

    const noop = asyncPipe([
      mergeOnChainOntoOffChain,
      (request) => ({
        action: "NO_OP",
        payload: request,
      }),
    ]);

    const pipeline = asyncPipe([
      fetchOnChainCounterpart,
      cond([
        [requestNotified, handleNotifiedRequest],
        [requestStatusChanged, updateOffChainRequest],
        [() => true, noop],
      ]),
    ]);

    const notifiedRequests = await fetchRequestsByChainIdAndStatus({ status: Status.Notified, chainId });

    console.info({ data: map(pick(["questionId", "requester"]), notifiedRequests) }, "Fetched notified requests");

    const results = await P.allSettled(map(pipeline, notifiedRequests));

    const groupQuestionsOrErrorMessage = (acc, r) => {
      if (r.status === "rejected") {
        return [...acc, r.reason?.message];
      }

      const questionId = r.value?.payload?.questionId ?? "<not set>";
      const requester = r.value?.payload?.requester ?? "<not set>";
      return [...acc, { questionId, requester }];
    };
    const toTag = (r) => (r.status === "rejected" ? "FAILURE" : r.value?.action);
    const stats = reduceBy(groupQuestionsOrErrorMessage, [], toTag, results);

    console.info(stats, "Processed notified requests");

    return stats;
  }

  async function fetchOnChainCounterpart(offChainRequest) {
    const { questionId, requester } = offChainRequest;

    const onChainRequest = await homeChainApi.getRequest({ questionId, requester });

    return [offChainRequest, onChainRequest];
  }
}
Example #20
Source File: checkArbitratorAnswers.js    From cross-chain-realitio-proxy with MIT License 4 votes vote down vote up
export default async function checkArbitratorAnswers({ homeChainApi }) {
  const chainId = await homeChainApi.getChainId();

  await checkUntrackedArbitratorAnswers();
  await checkCurrentArbitratorAnswers();

  async function checkUntrackedArbitratorAnswers() {
    const [fromBlock, toBlock] = await P.all([getBlockHeight("RULED_REQUESTS"), homeChainApi.getBlockNumber()]);

    const untrackedNotifiedRequests = filter(
      ({ status }) => [Status.AwaitingRuling, Status.Ruled].includes(status),
      await homeChainApi.getNotifiedRequests({ fromBlock, toBlock })
    );

    await saveRequests(untrackedNotifiedRequests);

    const blockHeight = toBlock + 1;
    await updateBlockHeight({ key: "RULED_REQUESTS", blockHeight });
    console.info({ blockHeight }, "Set RULED_REQUESTS block height");

    const stats = {
      data: map(pick(["questionId", "chainId"]), untrackedNotifiedRequests),
      fromBlock,
      toBlock,
    };

    console.info(stats, "Found new requests with arbitrator answers");

    return stats;
  }

  async function checkCurrentArbitratorAnswers() {
    const requestFinishedOrCanceled = ([_, onChainRequest]) =>
      [Status.None, Status.Finished].includes(onChainRequest.status);
    const removeOffChainRequest = asyncPipe([
      mergeOnChainOntoOffChain,
      removeRequest,
      (request) => ({
        action: "OFF_CHAIN_REQUEST_REMOVED",
        payload: request,
      }),
    ]);

    const requestRuled = ([_, onChainArbitration]) => onChainArbitration.status === Status.Ruled;
    const reportArbitrationAnswer = asyncPipe([
      mergeOnChainOntoOffChain,
      homeChainApi.reportArbitrationAnswer,
      (request) => ({
        action: "ARBITRATION_ANSWER_REPORTED",
        payload: request,
      }),
    ]);

    const noop = asyncPipe([
      mergeOnChainOntoOffChain,
      (arbtration) => ({
        action: "NO_OP",
        payload: arbtration,
      }),
    ]);

    const requestsWithRuling = await fetchRequestsByChainId({ status: Status.Ruled, chainId });

    console.info(
      { data: map(pick(["questionId", "requester"]), requestsWithRuling) },
      "Fetched requests which received the arbitration ruling"
    );

    const pipeline = asyncPipe([
      fetchOnChainCounterpart,
      cond([
        [requestFinishedOrCanceled, removeOffChainRequest],
        [requestRuled, reportArbitrationAnswer],
        [() => true, noop],
      ]),
    ]);

    const results = await P.allSettled(map(pipeline, requestsWithRuling));

    const groupQuestionsOrErrorMessage = (acc, r) => {
      if (r.status === "rejected") {
        return [...acc, r.reason?.message];
      }

      const questionId = r.value?.payload?.questionId ?? "<not set>";
      const requester = r.value?.payload?.requester ?? "<not set>";
      return [...acc, { questionId, requester }];
    };
    const toTag = (r) => (r.status === "rejected" ? "FAILURE" : r.value?.action);
    const stats = reduceBy(groupQuestionsOrErrorMessage, [], toTag, results);

    console.info(stats, "Processed requests with ruling");
  }

  async function fetchOnChainCounterpart(offChainRequest) {
    const { questionId, requester } = offChainRequest;

    const onChainRequest = await homeChainApi.getRequest({ questionId, requester });

    return [offChainRequest, onChainRequest];
  }
}