From b78633f67fb3d505bb3a6388fb2ad215d74590f8 Mon Sep 17 00:00:00 2001 From: trevorjtclarke Date: Thu, 3 Feb 2022 10:40:49 -0800 Subject: [PATCH] added high res timer for triggers, testing all features --- src/configuration.js | 1 + src/tasks.js | 5 ++-- src/triggers.js | 70 ++++++++++++++++++++++++++++---------------- src/util.js | 2 +- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/configuration.js b/src/configuration.js index 406759e..4945266 100644 --- a/src/configuration.js +++ b/src/configuration.js @@ -19,6 +19,7 @@ export const NEAR_ENV = process.env.NEAR_ENV || 'testnet' export const LOG_LEVEL = process.env.LOG_LEVEL || 'info' export const BETA_FEATURES = process.env.BETA_FEATURES === 'true' ? true : false export const WAIT_INTERVAL_MS = process.env.WAIT_INTERVAL_MS ? parseInt(`${process.env.WAIT_INTERVAL_MS}`) : 30000 +export const TRIGGER_INTERVAL_MS = process.env.TRIGGER_INTERVAL_MS ? parseInt(`${process.env.TRIGGER_INTERVAL_MS}`) : 10000 export const AGENT_ACCOUNT_ID = process.env.AGENT_ACCOUNT_ID || 'croncat-agent' export const AGENT_MIN_TASK_BALANCE = utils.format.parseNearAmount(`${process.env.AGENT_MIN_TASK_BALANCE || '1'}`) // Default: 1_000_000_000_000_000_000_000_000 (1 NEAR) export const AGENT_AUTO_REFILL = process.env.AGENT_AUTO_REFILL === 'true' ? true : false diff --git a/src/tasks.js b/src/tasks.js index 0be60eb..ed38e13 100644 --- a/src/tasks.js +++ b/src/tasks.js @@ -1,5 +1,4 @@ import * as config from './configuration' -import * as agent from './agent' import * as util from './util' import { utils } from 'near-api-js' import chalk from 'chalk' @@ -108,8 +107,8 @@ export const proxyCall = async () => { try { const res = await manager.proxy_call({ args: {}, - gas: BASE_GAS_FEE, - amount: BASE_ATTACHED_PAYMENT, + gas: config.BASE_GAS_FEE, + amount: config.BASE_ATTACHED_PAYMENT, }) if (config.LOG_LEVEL === 'debug') console.log(res) if (config.LOG_LEVEL === 'debug') console.log(`${chalk.yellowBright('TX:' + res.transaction_outcome.id)}`) diff --git a/src/triggers.js b/src/triggers.js index 5410baf..889e739 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -1,10 +1,13 @@ import * as config from './configuration' import * as util from './util' +import chalk from 'chalk' let cache = [] -let lastCacheCheckTs = +new Date() // Update cache every slot duration (60 blocks) const CACHE_DELAY = 60 * 1000 +const TIME_GRANULARITY = 100 * 10000 +let lastCacheCheckTs = +new Date() - CACHE_DELAY +let triggersProcessed = 0 // Get trigger list export const getTriggers = async (from_index = 0, limit = 100) => { @@ -14,7 +17,6 @@ export const getTriggers = async (from_index = 0, limit = 100) => { try { // Only get task hashes my agent can execute triggers = await manager.get_triggers({ from_index: `${from_index}`, limit: `${limit}` }) - console.log('triggers', triggers) } catch (e) { if (config.LOG_LEVEL === 'debug') console.log('getTriggers', e) } @@ -24,6 +26,7 @@ export const getTriggers = async (from_index = 0, limit = 100) => { // Get trigger cache // cache for current triggers list & configs +// TODO: Add logging & stats logging if desired export const getAllTriggers = async () => { if (lastCacheCheckTs + CACHE_DELAY > +new Date()) return cache cache = [] @@ -40,29 +43,37 @@ export const getAllTriggers = async () => { } await loopRetrieve() lastCacheCheckTs = +new Date() + + // stats logging + const manager = await util.getCronManager() + console.log(`${chalk.gray(new Date().toISOString())} ${chalk.gray('[' + manager.account.connection.networkId.toUpperCase() + ']')} Available Triggers: ${chalk.blueBright(cache.length)}, Processed: ${chalk.yellow(triggersProcessed)}`) + // reset after log + triggersProcessed = 0 + + return cache } // Call any contract with a "view" RPC call export const viewTrigger = async trigger_hash => { - const trigger = cache[triggerHash] + let trigger let outcome = false + cache.forEach(t => { + if (t.hash === trigger_hash) trigger = {...t} + }) + if (!trigger) return outcome try { // Check if the trigger evaluates to true or false const res = await util.queryRpc(`${trigger.contract_id}`, `${trigger.function_id}`, null, null, trigger.arguments) - if (!res) this.validFunctionId = false - if (res) { - // looking for specific error message to confirm function exists - if (res.search('MethodNotFound') > -1) { - this.validFunctionId = false - } else this.validFunctionId = true - } - console.log('callTrigger res', res) - // check outcome === true + if (config.LOG_LEVEL === 'debug') console.log('callTrigger res', res) + if (!res) outcome = false // res should return a standard payload: (bool, Base64VecU8) + if (typeof res === 'boolean') outcome = res + if (typeof res === 'object' && typeof res[0] === 'boolean') outcome = res[0] } catch (e) { if (config.LOG_LEVEL === 'debug') console.log('callTrigger', e) } + triggersProcessed += 1 return outcome } @@ -71,12 +82,15 @@ export const viewTrigger = async trigger_hash => { // NOTE: Must be careful here, as fast & high amounts of triggers could drain balance quickly! export const callTrigger = async trigger_hash => { const manager = await util.getCronManager() - const trigger = cache[triggerHash] try { // Only get task hashes my agent can execute - const res = await manager.proxy_conditional_call({ trigger_hash }) - console.log('callTrigger res', res) + const res = await manager.proxy_conditional_call({ + args: { trigger_hash }, + gas: config.BASE_GAS_FEE, + amount: config.BASE_ATTACHED_PAYMENT, + }) + if (config.LOG_LEVEL === 'debug') console.log('callTrigger res', res) } catch (e) { if (config.LOG_LEVEL === 'debug') console.log('callTrigger', e) } @@ -87,25 +101,29 @@ export const callTrigger = async trigger_hash => { // NOTE: This is built without batching, could be implemented in the future export async function run() { const allTriggers = await getAllTriggers() - console.log('allTriggers', allTriggers); // If there aren't any triggers, wait a while before checking for more - if (!allTriggers || allTriggers.length <= 0) { - setTimeout(run, CACHE_DELAY) - return - } + if (!allTriggers || allTriggers.length <= 0) return setTimeout(run, CACHE_DELAY) + + // speed range + // protect the runtime of triggers with high resolution timer + // NOTE: Probably overkill, but allows for easy acceleration if needed to accommodate faster block times + const sr_start = process.hrtime() // Logic: // - Call every trigger to check if any triggers are evaluating to TRUE // - If TRUE, do 'proxy_conditional_call' call - await allTriggers.reduce(async (trigger, i) => { + await allTriggers.reduce(async (ctx, trigger, i) => { const shouldCall = await viewTrigger(trigger.hash) if (shouldCall) await callTrigger(trigger.hash) - return [...trigger, i + 1] + return [...ctx, i + 1] }, []) - // TODO: Check if RPC processing time too longer than interval, if so do next immediately - // TODO: Add logging & stats logging if desired - // Wait, then loop again. - setTimeout(run, config.TRIGGER_INTERVAL_MS) + // end speed range + const sr_end = process.hrtime(sr_start) + + // Check if RPC processing time too longer than interval, if so do next immediately + const exec_duration = sr_end[1] / TIME_GRANULARITY + if (exec_duration > config.TRIGGER_INTERVAL_MS) run() + else setTimeout(run, Math.abs(config.TRIGGER_INTERVAL_MS - exec_duration)) } diff --git a/src/util.js b/src/util.js index e820ed4..3cb965c 100644 --- a/src/util.js +++ b/src/util.js @@ -69,7 +69,7 @@ export const queryRpc = async (account_id, method_name, args, options = {}, args try { // TODO: Test this, setup using connection pool - res = await Near.connection.provider.query({ + res = await Near.client.connection.provider.query({ request_type: 'call_function', finality: 'final', account_id,