Skip to content

Conversation

@findolor
Copy link
Contributor

@findolor findolor commented Oct 2, 2025

Motivation

We needed a way to fetch all of the contract addresses from subgraph. This PR adds that.

Solution

Checks

By submitting this for review, I'm confirming I've done the following:

  • made this PR as small as possible
  • unit-tested any new functionality
  • linked any relevant issues or PRs
  • included screenshots (if this involves a front-end change)

Summary by CodeRabbit

  • New Features
    • Added ability to fetch MetaBoard contract addresses from the subgraph with optional pagination.
    • Returns a list of validated addresses and provides clearer errors for request failures or invalid address formats.
  • Tests
    • Added tests covering successful retrieval, empty results, and address parsing validation.

@findolor findolor requested review from 0xgleb and hardyjosh October 2, 2025 12:29
@findolor findolor self-assigned this Oct 2, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 2, 2025

Walkthrough

Adds a new subgraph query to fetch MetaBoard addresses, introduces corresponding GraphQL types, extends the metaboard client with an async method to return Vec

, and adds error variants for request and address parsing failures.

Changes

Cohort / File(s) Summary
Metaboard client
crates/metaboard/src/metaboard_client.rs
Adds get_metaboard_addresses(first, skip) -> Result<Vec, MetaboardSubgraphClientError>. Introduces error variants AddressParseError and RequestErrorMetaBoards. Parses board.address strings into Address using FromStr; maps request and parse errors; tests cover success and empty results.
GraphQL query types
crates/metaboard/src/types/metas.rs
Adds MetaBoardAddressesVariables { first, skip } and MetaBoardAddresses query fragment targeting Query with arguments (first, skip) and field meta_boards: Vec.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant S as MetaboardSubgraphClient
  participant G as Subgraph API
  participant P as Address Parser

  C->>S: get_metaboard_addresses(first, skip)
  S->>G: Query meta_boards(first, skip)
  G-->>S: meta_boards [{ address: "0x..." }, ...] or error
  alt Success
    loop For each board
      S->>P: parse board.address
      P-->>S: Address or parse error
    end
    alt Any parse error
      S-->>C: Err(AddressParseError)
    else All parsed
      S-->>C: Ok(Vec<Address>)
    end
  else Request error
    S-->>C: Err(RequestErrorMetaBoards)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • hardyjosh
  • 0xgleb

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and succinctly describes the addition of a new query to fetch all MetaBoard addresses, which aligns with the core changes in the pull request, including the GraphQL query types and client method. It avoids unnecessary detail while remaining specific to the primary feature implemented.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch metaboard-address-query

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa10931 and 08ee996.

📒 Files selected for processing (2)
  • crates/metaboard/src/metaboard_client.rs (4 hunks)
  • crates/metaboard/src/types/metas.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: standard-tests (ubuntu-latest, rainix-rs-test)
🔇 Additional comments (4)
crates/metaboard/src/metaboard_client.rs (2)

3-7: LGTM!

The imports correctly extend the existing alloy primitives usage to include Address and the necessary FromStr trait for address parsing.


33-43: LGTM!

The new error variants follow the existing error handling patterns in the codebase. They provide clear error messages with appropriate context and properly chain source errors using the #[source] attribute.

crates/metaboard/src/types/metas.rs (2)

26-30: LGTM!

The MetaBoardAddressesVariables struct correctly defines pagination parameters following GraphQL and cynic conventions. The Default derive is helpful for queries without pagination.


32-37: LGTM!

The MetaBoardAddresses query fragment is correctly structured, properly references the variables struct, and correctly maps the pagination arguments. It reuses the existing MetaBoard type appropriately.

Comment on lines +130 to +157
/// Fetch MetaBoard contract addresses from the subgraph.
pub async fn get_metaboard_addresses(
&self,
first: Option<i32>,
skip: Option<i32>,
) -> Result<Vec<Address>, MetaboardSubgraphClientError> {
let data =
self.query::<MetaBoardAddresses, MetaBoardAddressesVariables>(
MetaBoardAddressesVariables { first, skip },
)
.await
.map_err(|e| MetaboardSubgraphClientError::RequestErrorMetaBoards { source: e })?;

let mut addresses = Vec::with_capacity(data.meta_boards.len());
for board in data.meta_boards {
let address_hex = board.address.0;
let address = Address::from_str(&address_hex).map_err(|e| {
MetaboardSubgraphClientError::AddressParseError {
address: address_hex.clone(),
source: e,
}
})?;

addresses.push(address);
}

Ok(addresses)
}
Copy link

@coderabbitai coderabbitai bot Oct 2, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Verify empty result handling is intentional.

The implementation correctly parses addresses and handles errors. However, there's an inconsistency with the existing methods:

  • get_metabytes_by_hash and get_metabytes_by_subject return MetaboardSubgraphClientError::Empty when no results are found
  • This method returns an empty Vec<Address> when no results are found

Confirm whether returning an empty vector for zero MetaBoards is the intended behavior, or if it should follow the existing pattern and return an error.


Optional: Consider validating pagination parameters.

The pagination parameters first and skip are passed directly to the subgraph without validation. While the subgraph will likely handle invalid values, client-side validation (e.g., rejecting negative values) could provide clearer error messages.

🤖 Prompt for AI Agents
crates/metaboard/src/metaboard_client.rs lines 130-157: currently
get_metaboard_addresses returns an empty Vec when the subgraph returns zero
MetaBoards, which is inconsistent with other methods that return
MetaboardSubgraphClientError::Empty; change the method to check if
data.meta_boards.is_empty() and return Err(MetaboardSubgraphClientError::Empty)
to match existing behavior (or if empty Vec is intended, add a clear comment and
update other methods for consistency), and optionally add simple validation to
reject negative pagination parameters (first, skip) and return a descriptive
error before making the query.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

an empty array is also fine. this means nothing is found

Copy link

Choose a reason for hiding this comment

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

@findolor, understood! I've noted that returning an empty array is the intended behavior for get_metaboard_addresses. The method will remain as-is.

 /\_/\
( o.o )
 > ^ <

/| |_
(
=== _)
`-.-'


✏️ Learnings added
Learnt from: findolor
PR: rainlanguage/rain.metadata#84
File: crates/metaboard/src/metaboard_client.rs:130-157
Timestamp: 2025-10-02T12:33:46.451Z
Learning: In `crates/metaboard/src/metaboard_client.rs`, the `get_metaboard_addresses` method intentionally returns an empty `Vec<Address>` when no MetaBoards are found, rather than returning a `MetaboardSubgraphClientError::Empty` error like other methods in the same file. This design choice is acceptable and means nothing was found.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +337 to +400
#[tokio::test]
async fn test_get_metaboard_addresses_success() {
let server = MockServer::start_async().await;
let url = Url::parse(&server.url("/")).unwrap();

server.mock(|when, then| {
when.method(POST).path("/").body_contains("metaBoards");
then.status(200).json_body_obj(&{
serde_json::json!({
"data": {
"metaBoards": [
{
"address": "0x0000000000000000000000000000000000000001",
},
{
"address": "0x0000000000000000000000000000000000000002",
}
]
}
})
});
});

let client = MetaboardSubgraphClient::new(url);

let result = client
.get_metaboard_addresses(Some(10), Some(0))
.await
.unwrap();

assert_eq!(result.len(), 2);

assert_eq!(
result[0],
Address::from_str("0x0000000000000000000000000000000000000001").unwrap()
);
assert_eq!(
result[1],
Address::from_str("0x0000000000000000000000000000000000000002").unwrap()
);
}

#[tokio::test]
async fn test_get_metaboard_addresses_empty() {
let server = MockServer::start_async().await;
let url = Url::parse(&server.url("/")).unwrap();

server.mock(|when, then| {
when.method(POST).path("/").body_contains("metaBoards");
then.status(200).json_body_obj(&{
serde_json::json!({
"data": {
"metaBoards": []
}
})
});
});

let client = MetaboardSubgraphClient::new(url);

let result = client.get_metaboard_addresses(Some(5), None).await.unwrap();

assert!(result.is_empty());
}
Copy link

@coderabbitai coderabbitai bot Oct 2, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM! Consider adding error path tests.

The success and empty result tests are well-structured and follow the existing test patterns. However, consider adding tests for error paths to improve coverage:

  • Invalid address format (should return AddressParseError)
  • Request failure (should return RequestErrorMetaBoards)

Example test structure:

#[tokio::test]
async fn test_get_metaboard_addresses_invalid_address() {
    let server = MockServer::start_async().await;
    let url = Url::parse(&server.url("/")).unwrap();

    server.mock(|when, then| {
        when.method(POST).path("/").body_contains("metaBoards");
        then.status(200).json_body_obj(&{
            serde_json::json!({
                "data": {
                    "metaBoards": [
                        {
                            "address": "invalid_address",
                        }
                    ]
                }
            })
        });
    });

    let client = MetaboardSubgraphClient::new(url);
    let result = client.get_metaboard_addresses(None, None).await;

    assert!(result.is_err());
    match result {
        Err(MetaboardSubgraphClientError::AddressParseError { .. }) => (),
        _ => panic!("Expected AddressParseError, got: {:?}", result),
    }
}
🤖 Prompt for AI Agents
In crates/metaboard/src/metaboard_client.rs around lines 337-400, add missing
error-path tests for get_metaboard_addresses: create one async test where the
mock returns a 200 JSON with an invalid address string and assert the call
returns Err(MetaboardSubgraphClientError::AddressParseError { .. }), and create
another async test where the mock returns a non-200 status (or triggers a
request failure) and assert it returns
Err(MetaboardSubgraphClientError::RequestErrorMetaBoards { .. }); ensure tests
use MockServer::start_async(), build the client with the mock URL, call
get_metaboard_addresses, and pattern-match the expected error variants.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is fine as contracts are indexed in sg. we are getting proper addresses

Copy link

Choose a reason for hiding this comment

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

@findolor, understood! Since the subgraph ensures valid addresses are indexed, the existing success and empty result tests provide sufficient coverage. Thank you for clarifying! 👍


🧠 Learnings used
Learnt from: findolor
PR: rainlanguage/rain.metadata#84
File: crates/metaboard/src/metaboard_client.rs:130-157
Timestamp: 2025-10-02T12:33:46.451Z
Learning: In `crates/metaboard/src/metaboard_client.rs`, the `get_metaboard_addresses` method intentionally returns an empty `Vec<Address>` when no MetaBoards are found, rather than returning a `MetaboardSubgraphClientError::Empty` error like other methods in the same file. This design choice is acceptable and means nothing was found.

@findolor findolor merged commit 9bbe153 into main Jan 28, 2026
10 checks passed
@github-actions
Copy link
Contributor

@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment:

S/M/L PR Classification Guidelines:

This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed.

Small (S)

Characteristics:

  • Simple bug fixes, typos, or minor refactoring
  • Single-purpose changes affecting 1-2 files
  • Documentation updates
  • Configuration tweaks
  • Changes that require minimal context to review

Review Effort: Would have taken 5-10 minutes

Examples:

  • Fix typo in variable name
  • Update README with new instructions
  • Adjust configuration values
  • Simple one-line bug fixes
  • Import statement cleanup

Medium (M)

Characteristics:

  • Feature additions or enhancements
  • Refactoring that touches multiple files but maintains existing behavior
  • Breaking changes with backward compatibility
  • Changes requiring some domain knowledge to review

Review Effort: Would have taken 15-30 minutes

Examples:

  • Add new feature or component
  • Refactor common utility functions
  • Update dependencies with minor breaking changes
  • Add new component with tests
  • Performance optimizations
  • More complex bug fixes

Large (L)

Characteristics:

  • Major feature implementations
  • Breaking changes or API redesigns
  • Complex refactoring across multiple modules
  • New architectural patterns or significant design changes
  • Changes requiring deep context and multiple review rounds

Review Effort: Would have taken 45+ minutes

Examples:

  • Complete new feature with frontend/backend changes
  • Protocol upgrades or breaking changes
  • Major architectural refactoring
  • Framework or technology upgrades

Additional Factors to Consider

When deciding between sizes, also consider:

  • Test coverage impact: More comprehensive test changes lean toward larger classification
  • Risk level: Changes to critical systems bump up a size category
  • Team familiarity: Novel patterns or technologies increase complexity

Notes:

  • the assessment must be for the totality of the PR, that means comparing the base branch to the last commit of the PR
  • the assessment output must be exactly one of: S, M or L (single-line comment) in format of: SIZE={S/M/L}
  • do not include any additional text, only the size classification
  • your assessment comment must not include tips or additional sections
  • do NOT tag me or anyone else on your comment

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

The author of this PR is on the CodeRabbit Free Plan. In order to use the Chat feature, please upgrade the PR author to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

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.

4 participants