Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: scripts for fetching plan execution statuses #5

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

oddaf
Copy link
Member

@oddaf oddaf commented Jul 24, 2024

Resolves #3

@oddaf oddaf requested review from amusingaxl and 0xdecr1pto July 24, 2024 20:42
Copy link
Contributor

@amusingaxl amusingaxl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good overall, I thought it would be way more complex than this due to LibNote.
I pointed a couple of issues with the data fetching strategy, and also we might need to figure out a way to reference the spell address itself, other than usr. A conforming spell uses the associated action as usr, however users (devs included), often refer to the spell main address.

scripts/list-pause-plans.js Outdated Show resolved Hide resolved
scripts/list-pause-plans.js Outdated Show resolved Hide resolved
scripts/list-pause-plans.js Outdated Show resolved Hide resolved
let tableData = prepareTableData(events, pause);

if (!argv.all && !argv.pending) {
tableData = tableData.length > 21 ? [tableData[0], ...tableData.slice(-20)] : tableData;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the suggestion about data sorting by newest values first is applied, this can be simplified as:

Suggested change
tableData = tableData.length > 21 ? [tableData[0], ...tableData.slice(-20)] : tableData;
tableData = tableData.length > 21 ? tableData.slice(0, 21) : tableData;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic was removed in favor of the fromBlock parameter: f6e02c3

if (!argv.all && !argv.pending) {
tableData = tableData.length > 21 ? [tableData[0], ...tableData.slice(-20)] : tableData;
} else if (argv.pending) {
tableData = tableData.filter(row => row[6] === "pending" || row[0] === "HASH");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison will fail because of mismatching cases:

Suggested change
tableData = tableData.filter(row => row[6] === "pending" || row[0] === "HASH");
tableData = tableData.filter(row => row[6] === "PENDING" || row[0] === "HASH");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in general, I would prefer to keep the presentation layer as simple as possible and do all filtering when data is being fetched.
This allows that part of the script to be reused by a front-end, which would not need to re-implement this sort of logic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e37ee56, also separated all the filtering from presentation layer 56872ae and 4f032ba


### 2. Run scripts

List the 20 most recent plans in `MCD_PAUSE`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that instead of fixing an arbitrary number of events to return, we could maybe let the user specify how many blocks they want to go in the past.

However, that introduces one edge case: events for the same [usr, tag, fax, eta] might be missing because of the from block. It's usually fine, as we can consider any subsequent exec or drop to have been preceded by a plan.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f6e02c3

scripts/list-pause-plans.js Outdated Show resolved Hide resolved
scripts/README.md Outdated Show resolved Hide resolved
scripts/README.md Outdated Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As commented in the README, usr usually refers to the DssSpellAction for conforming spells, not to the spell itself.
I believe the script should somehow return the associated spell address. I'm not entirely sure how to get that information, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] I'm not entirely sure how to get that information, though.

Oh, I just noticed that msg.sender is logged as topic 1:
https://vscode.blockscan.com/ethereum/0xbe286431454714f511008713973d3b053a2d38f3#L43

For conforming spells, this can be used to obtain the spell address.
Since it's not guaranteed, maybe the column should be named SENDER and the documentation should explain that this COULD be the spell address 🤷🏻‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 5843d92

@amusingaxl
Copy link
Contributor

Oh, I just noticed that there are both a script/ and a scripts/ directories now, maybe you want to move the entire content of this script into script/list-pause-plans/.

@oddaf
Copy link
Member Author

oddaf commented Aug 16, 2024

Oh, I just noticed that there are both a script/ and a scripts/ directories now, maybe you want to move the entire content of this script into script/list-pause-plans/.

I renamed the folder cli to avoid confusion. The script folder contains solidity scripts (foundry) so it didn't feel right to move this there, let me know what you think.

Comment on lines 43 to 45
if (!process.env.ETH_RPC_URL) {
console.warn("ETH_RPC_URL not set, falling back to a public RPC provider. For improved results set ETH_RPC_URL to a trusted node.");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also be a CLI parameter? Like --eth-rpc-url?
It could default to the env var if it's set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in a683b60

@DaiFoundation-DevOps
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

@amusingaxl amusingaxl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some additional suggestions


Pending plans in `MCD_PAUSE` since block 19420069
```
node list-pause-plans -pending --from-block 19420069
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
node list-pause-plans -pending --from-block 19420069
node list-pause-plans --pending --from-block 19420069

alias: 'e',
type: 'string',
description: 'Rpc URL - Falls back to ETH_RPC_URL env var, then to a public provider',
default: 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're being faithful to the description above, we should handle that here:

Suggested change
default: 0
default: process.env.ETH_RPC_URL || 'mainnet'

.option('eth-rpc-url', {
alias: 'e',
type: 'string',
description: 'Rpc URL - Falls back to ETH_RPC_URL env var, then to a public provider',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: 'Rpc URL - Falls back to ETH_RPC_URL env var, then to a public provider',
description: 'RPC URL - Falls back to `ETH_RPC_URL` env var, then to a public provider',

Comment on lines +21 to +22
.option('eth-rpc-url', {
alias: 'e',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to make the options align with Foundry:

image

I agree can be f'ing confusing, but ¯\_(ツ)_/¯:

Suggested change
.option('eth-rpc-url', {
alias: 'e',
.option('rpc-url', {
alias: ['fork-url', 'f'],

Comment on lines +45 to +51
function getProvider() {
const url = argv.ethRpcUrl || process.env.ETH_RPC_URL || "mainnet";
if (url == "mainnet") {
console.warn("Falling back to a public provider. For best experience set a custom RPC URL.");
}
return ethers.getDefaultProvider(url);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the suggestion above is applied, then this whole function becomes superfluous:

Suggested change
function getProvider() {
const url = argv.ethRpcUrl || process.env.ETH_RPC_URL || "mainnet";
if (url == "mainnet") {
console.warn("Falling back to a public provider. For best experience set a custom RPC URL.");
}
return ethers.getDefaultProvider(url);
}
if (argv.rpcUrl == "mainnet") {
console.warn("Falling back to a public provider. For best experience set a custom RPC URL.");
}

Comment on lines +55 to +56
const provider = getProvider();
const pause = new ethers.Contract(MCD_PAUSE, pauseABI, provider);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const provider = getProvider();
const pause = new ethers.Contract(MCD_PAUSE, pauseABI, provider);
const provider = getProvider();
const pause = new ethers.Contract(MCD_PAUSE, pauseABI, ethers.getDefaultProvider(argv.rpcUrl));

.alias('help', 'h')
.argv;

const MCD_PAUSE = "0xbE286431454714F511008713973d3B053A2d38f3";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This most likely belongs in the pause-plan-data.js file.

};
}

export function prepareData(events, contract, filter) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function conflates data processing with data viewing.
prepareData should return an array of objects instead and let the table-formatting to the upper level.

You can and should enrich the data here, (i.e.: figuring out the status), as this part of the data logic, however it should return the data in the most agnostic way possible.

Copy link
Contributor

@amusingaxl amusingaxl Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should probably be a façade function that hides all implementation details and returns plain data in this file.

From an information ownership perspective, it should also take care of instantiating the Pause contract.

All configurable options should be passed in as an options object:

const MCD_PAUSE = "0xbE286431454714F511008713973d3B053A2d38f3";

export const PlanStatus = {
    ALL: 'ALL',
    PENDING: 'PENDING',
    DROPPED: 'DROPPED',
    EXECUTED: 'EXECUTED',
}

// Considering 1 block every 12.5 secs
const FROM_1_YEAR_AGO = -2522880;

export async function getPausePlans(
  provider,
  { fromBlock = FROM_1_YEAR_AGO, status = PlanStatus.ALL } = {},
) {
  const pause = new ethers.Contract(MCD_PAUSE, pauseABI, provider);

  const events = await getFilteredEvents(pause, fromBlock);
  const filter = status === PlanStatus.ALL ? undefined : status;
  return prepareData(events, pause, filter);
}

This way it can be more easily consumed by different clients, being it the CLI in the other script or a 3rd party UI.

const pause = new ethers.Contract(MCD_PAUSE, pauseABI, provider);

const events = await getFilteredEvents(pause, argv.fromBlock);
let tableData = prepareData(events, pause, argv.pending ? "PENDING" : null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of importing prepareData, there should be a function in this file that will turn the enriched raw data into a table structure.

@amusingaxl
Copy link
Contributor

Oh, please remove the top-level package-lock.json file. There should be only 1 of those, within the cli/ directory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing script to fetch existing plans from pause
3 participants