Conversation
📝 WalkthroughWalkthroughThis PR bumps utxorpc dependencies in both balius-runtime and balius-sdk, refactors the transaction submit interface to accept wasm bytecode directly instead of file paths, replaces API key authentication with HTTP headers configuration, and adds a convenience coin() accessor method to the Utxo struct. Several test configurations are updated to align with the new authentication and wasm handling patterns. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@balius-runtime/tests/u5c-chainsync.rs`:
- Around line 25-30: The test currently commits a real API key in the headers
assignment (the "api-key" entry and the commented api_key line); remove the
literal string and instead read the secret from an environment variable (e.g.,
std::env::var("UTXORPC_API_KEY")) when constructing headers, and fail the test
early or skip it with a clear message if the env var is not set; also delete the
commented hardcoded key and rotate the leaked key in your secret store.
In `@balius-runtime/tests/u5c-ledger.rs`:
- Around line 10-14: Replace the hardcoded API key in the test that constructs
ledgers::u5c::Ledger::new with environment-based injection: read the API key
from an env var (e.g. U5C_API_KEY) and set headers only if the variable is
present, and if not present either omit the header and run the test against a
public endpoint or early-skip the test (use a test skip/return) instead of
embedding secrets; update the Config.headers construction where headers:
Some(HashMap::from([...])) currently includes the literal key to conditionally
build headers from the env var or skip the test.
In `@balius-sdk/src/qol.rs`:
- Around line 291-293: The match arm in coin() that handles
utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int currently does a
lossy cast (*z as u64) which wraps negatives; change it to attempt a fallible
conversion using u64::try_from(*z).ok() so negative or out-of-range i64 values
map to None instead of producing huge u64 balances, i.e. replace the direct cast
in the BigInt::Int arm with a try_from-based conversion and return that Option
result from coin().
In `@balius-sdk/src/txbuilder/dsl.rs`:
- Around line 349-351: The match arm for
utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) currently uses
a wrapping cast (*z as u64) which corrupts negative values for
coins_per_utxo_byte; replace that cast with u64::try_from(*z).ok() so negative
or out-of-range i64 values return None, and update the subsequent error handling
message that references coins_per_utxo_byte/min_lovelace to say the parameter is
invalid (not missing) when conversion fails; search for the match using
BigInt::Int and the code that computes min_lovelace/coins_per_utxo_byte to apply
this change.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
balius-runtime/Cargo.tomlbalius-runtime/src/lib.rsbalius-runtime/src/submit/u5c.rsbalius-runtime/tests/e2e.rsbalius-runtime/tests/u5c-chainsync.rsbalius-runtime/tests/u5c-ledger.rsbalius-sdk/Cargo.tomlbalius-sdk/src/qol.rsbalius-sdk/src/txbuilder/dsl.rsexamples/wallet/offchain/src/lib.rs
| headers: Some(HashMap::from([( | ||
| "api-key".to_string(), | ||
| "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(), | ||
| )])), | ||
| // api_key: "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(), | ||
| }; |
There was a problem hiding this comment.
Remove committed API key from test code.
Lines 25–30 expose a live credential (including in a comment). This is a security blocker; move it to environment/config secrets and rotate the leaked key.
🔐 Suggested fix
- let chainsync_config = drivers::chainsync::Config {
+ let api_key = std::env::var("UTXORPC_API_KEY")
+ .expect("UTXORPC_API_KEY must be set for integration test");
+
+ let chainsync_config = drivers::chainsync::Config {
endpoint_url: "https://mainnet.utxorpc-v0.demeter.run".to_string(),
headers: Some(HashMap::from([(
"api-key".to_string(),
- "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(),
+ api_key,
)])),
- // api_key: "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(),
};🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 29-29: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@balius-runtime/tests/u5c-chainsync.rs` around lines 25 - 30, The test
currently commits a real API key in the headers assignment (the "api-key" entry
and the commented api_key line); remove the literal string and instead read the
secret from an environment variable (e.g., std::env::var("UTXORPC_API_KEY"))
when constructing headers, and fail the test early or skip it with a clear
message if the env var is not set; also delete the commented hardcoded key and
rotate the leaked key in your secret store.
| let ledger = ledgers::u5c::Ledger::new(&ledgers::u5c::Config { | ||
| endpoint_url: "https://mainnet.utxorpc-v0.demeter.run".to_string(), | ||
| api_key: "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(), | ||
| headers: Some(HashMap::from([ | ||
| ("api-key".to_string(), "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string()), | ||
| ])), |
There was a problem hiding this comment.
Remove hardcoded API key from test source.
Line 13 embeds a real credential in plaintext. This is a secret-leak risk and should be replaced with environment-based injection (or test skip when missing).
🔐 Suggested fix
- let ledger = ledgers::u5c::Ledger::new(&ledgers::u5c::Config {
+ let api_key = match std::env::var("UTXORPC_API_KEY") {
+ Ok(v) => v,
+ Err(_) => {
+ eprintln!("Skipping test: UTXORPC_API_KEY is not set");
+ return;
+ }
+ };
+
+ let ledger = ledgers::u5c::Ledger::new(&ledgers::u5c::Config {
endpoint_url: "https://mainnet.utxorpc-v0.demeter.run".to_string(),
headers: Some(HashMap::from([
- ("api-key".to_string(), "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string()),
+ ("api-key".to_string(), api_key),
])),
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let ledger = ledgers::u5c::Ledger::new(&ledgers::u5c::Config { | |
| endpoint_url: "https://mainnet.utxorpc-v0.demeter.run".to_string(), | |
| api_key: "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string(), | |
| headers: Some(HashMap::from([ | |
| ("api-key".to_string(), "dmtr_utxorpc1wgnnj0qcfj32zxsz2uc8d4g7uclm2s2w".to_string()), | |
| ])), | |
| let api_key = match std::env::var("UTXORPC_API_KEY") { | |
| Ok(v) => v, | |
| Err(_) => { | |
| eprintln!("Skipping test: UTXORPC_API_KEY is not set"); | |
| return; | |
| } | |
| }; | |
| let ledger = ledgers::u5c::Ledger::new(&ledgers::u5c::Config { | |
| endpoint_url: "https://mainnet.utxorpc-v0.demeter.run".to_string(), | |
| headers: Some(HashMap::from([ | |
| ("api-key".to_string(), api_key), | |
| ])), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@balius-runtime/tests/u5c-ledger.rs` around lines 10 - 14, Replace the
hardcoded API key in the test that constructs ledgers::u5c::Ledger::new with
environment-based injection: read the API key from an env var (e.g. U5C_API_KEY)
and set headers only if the variable is present, and if not present either omit
the header and run the test against a public endpoint or early-skip the test
(use a test skip/return) instead of embedding secrets; update the Config.headers
construction where headers: Some(HashMap::from([...])) currently includes the
literal key to conditionally build headers from the env var or skip the test.
| utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => { | ||
| Some(*z as u64) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the qol.rs file
find . -name "qol.rs" -type fRepository: txpipe/balius
Length of output: 80
🏁 Script executed:
# Check the structure of the repository
git ls-files | head -20Repository: txpipe/balius
Length of output: 552
🏁 Script executed:
# Search for the coin() method in the codebase
rg "fn coin" -A 15Repository: txpipe/balius
Length of output: 843
🏁 Script executed:
# Look for the utxorpc-spec dependency version
fd "Cargo.toml" | xargs grep -l "utxorpc" 2>/dev/nullRepository: txpipe/balius
Length of output: 140
🏁 Script executed:
# Check the balius-sdk Cargo.toml for utxorpc-spec version
cat balius-sdk/Cargo.tomlRepository: txpipe/balius
Length of output: 892
🏁 Script executed:
# Search for BigInt type definition in the codebase
rg "enum BigInt" -A 10Repository: txpipe/balius
Length of output: 39
🏁 Script executed:
# Also search for where BigInt is imported from
rg "BigInt::" | head -20Repository: txpipe/balius
Length of output: 673
🏁 Script executed:
# Check how BigInt::Int is handled in dsl.rs
rg "BigInt::Int" -B 3 -A 3 balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 347
🏁 Script executed:
# Let's also check the full context around the coin() method in qol.rs
cat -n balius-sdk/src/qol.rs | sed -n '280,300p'Repository: txpipe/balius
Length of output: 750
🌐 Web query:
utxorpc-spec 0.18.1 Rust BigInt::Int type definition cardano
💡 Result:
In utxorpc-spec Rust crate v0.18.1, Cardano’s BigInt::Int is the Int(i64) variant of the generated enum:
pub enum BigInt {
Int(i64),
BigUInt(Bytes),
BigNInt(Bytes),
}So the BigInt::Int payload/type is i64. [1]
🏁 Script executed:
# Check the similar pattern in dsl.rs to see if it has the same issue
cat -n balius-sdk/src/txbuilder/dsl.rs | grep -B 5 -A 5 "BigInt::Int"Repository: txpipe/balius
Length of output: 512
Fix lossy cast in coin() extraction.
Line 292 casts an i64 to u64 without bounds checking. Since BigInt::Int holds i64 values from utxorpc-spec 0.18.1, negative values will wrap to very large u64s, producing incorrect balances. Use u64::try_from(*z).ok() instead to properly handle out-of-range values.
💡 Suggested fix
impl<D> Utxo<D> {
pub fn coin(&self) -> u64 {
self.utxo
.coin
.as_ref()
.and_then(|x| {
x.big_int.as_ref().and_then(|y| match y {
utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => {
- Some(*z as u64)
+ u64::try_from(*z).ok()
}
_ => None,
})
})
.unwrap_or_default()
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => { | |
| Some(*z as u64) | |
| } | |
| utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => { | |
| u64::try_from(*z).ok() | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@balius-sdk/src/qol.rs` around lines 291 - 293, The match arm in coin() that
handles utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int currently
does a lossy cast (*z as u64) which wraps negatives; change it to attempt a
fallible conversion using u64::try_from(*z).ok() so negative or out-of-range i64
values map to None instead of producing huge u64 balances, i.e. replace the
direct cast in the BigInt::Int arm with a try_from-based conversion and return
that Option result from coin().
| utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => { | ||
| Some(*z as u64) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the file and examine the specific lines
find . -name "dsl.rs" -path "*/txbuilder/*" | head -5Repository: txpipe/balius
Length of output: 90
🏁 Script executed:
# Get a broader context around the lines in question
sed -n '340,365p' balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 1016
🏁 Script executed:
# Search for all occurrences of coins_per_utxo_byte in the file
rg "coins_per_utxo_byte" balius-sdk/src/txbuilder/dsl.rs -A 5 -B 5Repository: txpipe/balius
Length of output: 1026
🏁 Script executed:
# Let's check the type definition of BigInt::Int to confirm the type
rg "BigInt::Int" balius-sdk/src/txbuilder/dsl.rs -B 10 -A 10 | head -40Repository: txpipe/balius
Length of output: 881
🏁 Script executed:
# Check if utxorpc-spec is in the dependencies
find . -name "Cargo.toml" -exec grep -l "utxorpc" {} \;Repository: txpipe/balius
Length of output: 146
🏁 Script executed:
# Check the Cargo.toml for utxorpc-spec version
grep -A 10 "utxorpc" balius-sdk/Cargo.toml | head -20Repository: txpipe/balius
Length of output: 521
🏁 Script executed:
# Check around lines 355-357 to see if there's a similar pattern
sed -n '355,365p' balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 513
🏁 Script executed:
# Search for type definition of BigInt in the dependencies/source
rg "BigInt" balius-sdk/Cargo.lock -A 3 | head -30Repository: txpipe/balius
Length of output: 118
🏁 Script executed:
# Check if there's a utxorpc-spec definition file in the repo
find . -path "*/utxorpc*" -name "*.rs" | head -10Repository: txpipe/balius
Length of output: 39
🏁 Script executed:
# Look at the imports in dsl.rs to understand the full path
head -50 balius-sdk/src/txbuilder/dsl.rs | grep -E "use|import"Repository: txpipe/balius
Length of output: 261
🏁 Script executed:
# Let's check for the second occurrence mentioned "Also applies to: 355-357"
# First get the full context around these lines
sed -n '349,365p' balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 730
🏁 Script executed:
# Search for all similar patterns in the file that might have the same issue
rg "as u64" balius-sdk/src/txbuilder/dsl.rs -B 5 -A 2Repository: txpipe/balius
Length of output: 986
🏁 Script executed:
# Check the struct/module definitions to understand the full picture
ast-grep --pattern 'BigInt::Int($_) => { $$$ }'Repository: txpipe/balius
Length of output: 39
🌐 Web query:
utxorpc-spec 0.18.1 BigInt::Int type definition Cardano coins_per_utxo_byte
💡 Result:
In utxorpc-spec 0.18.1 (Rust bindings):
BigIntis an enum whoseIntvariant is defined asInt(i64)(with the other variantsBigUInt(Bytes)andBigNInt(Bytes)). [1]- In Cardano protocol parameters (
PParams),coins_per_utxo_byteis typed asOption<BigInt>. [2]
Implication: coins_per_utxo_byte may be represented as Some(BigInt::Int(<i64>)) when it fits in 64-bit signed range, otherwise via the big-(u)int byte variants. [1][2]
Sources: [1] [2]
🏁 Script executed:
# Let's check if there are other similar patterns with BigInt::Int casting
rg "BigInt::Int" balius-sdk/src/txbuilder/dsl.rs -B 3 -A 3Repository: txpipe/balius
Length of output: 347
🏁 Script executed:
# Count all occurrences of "as u64" specifically after BigInt::Int
grep -n "as u64" balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 272
🏁 Script executed:
# Let's look at the complete function to understand both locations
sed -n '330,375p' balius-sdk/src/txbuilder/dsl.rsRepository: txpipe/balius
Length of output: 1667
🏁 Script executed:
# Search for other occurrences of BigInt in the codebase that might match the "Also applies to" claim
rg "BigInt::" balius-sdk/src/txbuilder/dsl.rs -nRepository: txpipe/balius
Length of output: 150
🏁 Script executed:
# Check if there might be another function with similar pattern
rg "as u64" balius-sdk/src/txbuilder/ -B 5 -A 2Repository: txpipe/balius
Length of output: 2380
🏁 Script executed:
# Verify the line numbers more carefully - check what's at lines 355-357
sed -n '354,358p' balius-sdk/src/txbuilder/dsl.rs | cat -nRepository: txpipe/balius
Length of output: 330
Replace unsafe cast with proper validation for coins_per_utxo_byte.
Line 350 uses as u64 to cast an i64, which wraps negative values and corrupts min_lovelace calculation. Use u64::try_from(*z).ok() to reject negative/invalid values, and update the error message to reflect that the parameter is invalid rather than missing.
💡 Suggested fix
.and_then(|x| {
x.big_int.as_ref().and_then(|y| match y {
utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) => {
- Some(*z as u64)
+ u64::try_from(*z).ok()
}
_ => None,
})
})
.ok_or(BuildError::LedgerError(
- "Missing coins_per_utxo_byte protocol parameter".to_string(),
+ "Invalid coins_per_utxo_byte protocol parameter".to_string(),
))?;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@balius-sdk/src/txbuilder/dsl.rs` around lines 349 - 351, The match arm for
utxorpc_spec::utxorpc::v1alpha::cardano::big_int::BigInt::Int(z) currently uses
a wrapping cast (*z as u64) which corrupts negative values for
coins_per_utxo_byte; replace that cast with u64::try_from(*z).ok() so negative
or out-of-range i64 values return None, and update the subsequent error handling
message that references coins_per_utxo_byte/min_lovelace to say the parameter is
invalid (not missing) when conversion fails; search for the match using
BigInt::Int and the code that computes min_lovelace/coins_per_utxo_byte to apply
this change.
Summary by CodeRabbit
New Features
Bug Fixes
Chores