-
Notifications
You must be signed in to change notification settings - Fork 182
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
feat(sozo) : multicall from CLI #2679
base: main
Are you sure you want to change the base?
Conversation
Added multi-call support to sozo exceute command
WalkthroughOhayo, sensei! The changes in this pull request modify the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ExecuteArgs
participant CallArgs
participant Invoker
User->>ExecuteArgs: Execute command with multiple calls
ExecuteArgs->>CallArgs: Parse calls into CallArgs instances
CallArgs->>ExecuteArgs: Return parsed CallArgs
ExecuteArgs->>Invoker: Invoke multiple calls
Invoker->>ExecuteArgs: Return execution results
ExecuteArgs->>User: Return results of multiple calls
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
Hey @glihm , I did what I could from my side. |
Can you suggest me changes in the current file to resolve these conflicts |
It will be big help |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
bin/sozo/src/commands/execute.rs (1)
65-65
: Ensure consistency in error messages.Ohayo, sensei! The error message at line 65 refers to
<CONTRACT_NAME>
, while the help text uses<CONTRACT_ADDRESS/TAG>
. To maintain clarity and consistency, consider updating the error message to match the format in the help text.Apply this diff to update the error message:
-return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); +return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,..."));
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
bin/sozo/src/commands/execute.rs
(3 hunks)
🔇 Additional comments (1)
bin/sozo/src/commands/execute.rs (1)
72-74
: Reevaluate entrypoint name validation.
Ohayo, sensei! The current validation only allows alphanumeric characters and underscores in entrypoint names. Depending on the system's requirements, entrypoint names might include additional characters or be case-sensitive. Please verify if this restriction aligns with the allowed naming conventions.
If you need assistance adjusting the validation logic, I'm here to help!
tag_or_address: parts[0].parse()?, | ||
entrypoint: entrypoint.to_string(), | ||
calldata: if parts.len() > 2 { Some(parts[2..].join(",")) } else { None }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve calldata parsing to handle commas within arguments.
Ohayo, sensei! The current parsing logic joins calldata arguments with commas, which may cause issues if an argument itself contains a comma. Consider enhancing the parsing mechanism to handle such cases, possibly by using a different delimiter or supporting argument escaping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the modification.
Some other comments, and if you could rebase on dojo/main that would be perfect to then prepare for merging if the next iteration is ok. 👍
If you have questions, reach out to me on the dojo discord.
bin/sozo/src/commands/execute.rs
Outdated
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let s = s.trim(); | ||
if s.is_empty() { | ||
return Err(anyhow!("Empty call string")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call args may be empty, some entrypoints don't take any arguments.
bin/sozo/src/commands/execute.rs
Outdated
.get_contract_address(selector) | ||
.ok_or_else(|| anyhow!("Contract {descriptor} not found in the world diff.")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the way you can get the contract address, since the world diff is not always computed if we have a local manifest.
dojo/bin/sozo/src/commands/execute.rs
Line 81 in 81e9707
let contracts = utils::contracts_from_manifest_or_diff( |
… are empty call args
Hey @glihm , I did the changes you suggested. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (4)
bin/sozo/src/commands/execute.rs (4)
23-31
: Enhance help text clarity for better user experience.Ohayo, sensei! The help text could be more descriptive about the optional nature of calldata and include multiple examples covering different scenarios.
- help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,... (ex: dojo_examples:actions,execute,1,2)" + help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>[,<ARG1>,<ARG2>,...]\n\ + Examples:\n\ + - With args: dojo_examples:actions,execute,1,2\n\ + - Without args: my_contract,initialize\n\ + - Multiple calls: contract1,method1,1 contract2,method2"
61-78
: Consider enhancing argument parsing robustness.Ohayo, sensei! While the current implementation handles basic parsing well, consider these improvements:
- Support for quoted arguments to handle special characters
- Proper error messages for malformed inputs
- Trimming of whitespace for each argument, not just the first part
fn from_str(s: &str) -> Result<Self, Self::Err> { let s = s.trim(); - let parts: Vec<&str> = s.split(',').collect(); + let parts: Vec<&str> = s + .split(',') + .map(str::trim) + .collect(); if parts.len() < 2 { - return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); + return Err(anyhow!( + "Invalid call format. Expected at least contract name and entrypoint.\n\ + Format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>[,<ARG1>,<ARG2>,...]" + )); }
136-172
: Consider adding safeguards for multiple calls.Ohayo, sensei! While the implementation handles multiple calls well, consider adding:
- Maximum batch size limit to prevent excessive gas usage
- Gas estimation before execution
- Progress feedback for multiple calls
+ const MAX_BATCH_SIZE: usize = 10; // Adjust based on requirements + let call_args_list: Vec<CallArgs> = self.calls.iter() .map(|s| s.parse()) .collect::<Result<Vec<_>>>()?; + if call_args_list.len() > MAX_BATCH_SIZE { + return Err(anyhow!( + "Batch size {} exceeds maximum allowed size of {}", + call_args_list.len(), + MAX_BATCH_SIZE + )); + } for call_args in call_args_list {
157-161
: Enhance error handling for calldata parsing.Ohayo, sensei! The calldata parsing could benefit from more descriptive error messages and validation:
let calldata = if let Some(cd) = call_args.calldata { - calldata_decoder::decode_calldata(&cd)? + calldata_decoder::decode_calldata(&cd).map_err(|e| { + anyhow!( + "Failed to decode calldata '{}' for entrypoint '{}': {}", + cd, + call_args.entrypoint, + e + ) + })? } else { vec![] };
bin/sozo/src/commands/execute.rs
Outdated
fn resolve_contract_address( | ||
descriptor: &ResourceDescriptor, | ||
world_diff: &WorldDiff, | ||
) -> Result<Address> { | ||
match descriptor { | ||
ResourceDescriptor::Address(address) => Ok(*address), | ||
ResourceDescriptor::Tag(tag) => { | ||
let contracts = utils::contracts_from_manifest_or_diff( | ||
self.account.clone(), | ||
self.starknet.clone(), | ||
self.world, | ||
&ws, | ||
self.diff, | ||
) | ||
.await?; | ||
|
||
(contracts.get(tag).map(|c| c.address), contracts) | ||
} | ||
ResourceDescriptor::Name(_) => { | ||
unimplemented!("Expected to be a resolved tag with default namespace.") | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix contract address resolution implementation.
Ohayo, sensei! There are several issues in this function:
- The function uses
self
but is not a method (lines 90-92) - The
Name
variant is unimplemented but should handle the default namespace case
fn resolve_contract_address(
descriptor: &ResourceDescriptor,
world_diff: &WorldDiff,
+ options: &ExecuteArgs,
+ ws: &Workspace,
) -> Result<Address> {
match descriptor {
ResourceDescriptor::Address(address) => Ok(*address),
ResourceDescriptor::Tag(tag) => {
let contracts = utils::contracts_from_manifest_or_diff(
- self.account.clone(),
- self.starknet.clone(),
- self.world,
+ options.account.clone(),
+ options.starknet.clone(),
+ options.world,
&ws,
- self.diff,
+ options.diff,
)
.await?;
(contracts.get(tag).map(|c| c.address), contracts)
}
ResourceDescriptor::Name(name) => {
- unimplemented!("Expected to be a resolved tag with default namespace.")
+ // Handle name with default namespace
+ let tag = format!("{}:{}", profile_config.namespace.default, name);
+ resolve_contract_address(&ResourceDescriptor::Tag(tag), world_diff, options, ws)
}
}
}
Committable suggestion skipped: line range outside the PR's diff.
Added multi-call support to sozo exceute command
… are empty call args
…/dojo into Sozo-mutiple-execute
Ok @glihm , after resolving all the commits and conflicts I have done it |
And also @glihm , made the changes you requested |
Thanks @glihm , for being a kind maintainer and helping me. I have learned a lot from this PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (3)
bin/sozo/src/commands/execute.rs (3)
24-31
: Enhance help text clarity for better user experience.Ohayo, sensei! While the help text provides a basic example, it could be more comprehensive. Consider adding:
- Multiple call examples
- Explanation of optional calldata
- Clear distinction between address and tag usage
- help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,... (ex: dojo_examples:actions,execute,1,2)" + help = "List of calls to execute. Each call format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>[,<ARG1>,<ARG2>,...]\n\ + Examples:\n\ + - Single call: dojo_examples:actions,execute,1,2\n\ + - Multiple calls: 0x123...,write,1 dojo_examples:other,read\n\ + Note: Arguments are optional. Both addresses and tags are supported."
46-51
: Consider adding validation methods to CallArgs.Ohayo, sensei! The struct looks good, but consider adding validation methods to encapsulate common checks:
impl CallArgs { pub fn validate(&self) -> Result<()> { if self.entrypoint.is_empty() { return Err(anyhow!("Empty entrypoint")); } if !self.entrypoint.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { return Err(anyhow!("Invalid entrypoint format")); } Ok(()) } }
138-175
: Consider adding batch size limits and progress feedback.Ohayo, sensei! While the multicall implementation is solid, consider these enhancements:
- Add a maximum batch size limit to prevent excessive gas usage
- Provide progress feedback for multiple calls
+ const MAX_BATCH_SIZE: usize = 20; // Adjust based on gas considerations + if call_args_list.len() > MAX_BATCH_SIZE { + return Err(anyhow!("Batch size exceeds maximum allowed ({})", MAX_BATCH_SIZE)); + } + println!("Processing {} calls...", call_args_list.len()); for (i, call_args) in call_args_list.iter().enumerate() { + println!("Processing call {}/{}: {}", i + 1, call_args_list.len(), call_args.entrypoint); // ... existing code ... }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
bin/sozo/src/commands/execute.rs
(3 hunks)
🔇 Additional comments (2)
bin/sozo/src/commands/execute.rs (2)
61-78
:
Improve calldata parsing robustness.
Ohayo, sensei! The current implementation has a limitation with commas in calldata, as noted in previous reviews. Consider using a more robust parsing approach:
- let parts: Vec<&str> = s.split(',').collect();
+ // Split only on first two commas for contract and entrypoint
+ let mut parts = s.splitn(3, ',');
+ let contract = parts.next().ok_or_else(|| anyhow!("Missing contract address/tag"))?;
+ let entrypoint = parts.next().ok_or_else(|| anyhow!("Missing entrypoint"))?;
+ let calldata = parts.next().map(|s| s.to_string());
+
Ok(CallArgs {
- tag_or_address: parts[0].parse()?,
- entrypoint: entrypoint.to_string(),
- calldata: if parts.len() > 2 { Some(parts[2..].join(",")) } else { None },
+ tag_or_address: contract.parse()?,
+ entrypoint: entrypoint.trim().to_string(),
+ calldata,
})
82-106
:
Address the unimplemented Name variant.
Ohayo, sensei! The Name variant is still unimplemented, as noted in previous reviews:
The implementation should handle the default namespace case as suggested in previous reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @Manush-2005!
I'll do a functional review later today, would you mind adjusting the formatting and checking your code compiles?
You have some scripts/
available that you can run to help you having everything ready for the CI. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (4)
bin/sozo/src/commands/execute.rs (4)
26-28
: Enhance help text for better clarity.Ohayo, sensei! The help text could be more helpful with:
- Example showing multiple calls
- Clarification that calldata is optional
#[arg( - help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,... (ex: dojo_examples:actions,execute,1,2)" + help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>[,<ARG1>,<ARG2>,...] \ + Multiple calls can be specified. Examples:\n\ + - Single call: dojo_examples:actions,execute,1,2\n\ + - Multiple calls: dojo_examples:actions,execute,1,2 other_contract:actions,execute" )]
58-72
: Add additional validation checks for robustness.Ohayo, sensei! Consider adding these validations:
- Check for empty tag/address in parts[0]
- Add maximum length check for entrypoint (e.g., 31 characters for Cairo)
- Consider trimming whitespace from all parts, not just the first one
fn from_str(s: &str) -> Result<Self, Self::Err> { let s = s.trim(); let parts: Vec<&str> = s.split(',').collect(); if parts.len() < 2 { return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); } + let tag_or_address = parts[0].trim(); + if tag_or_address.is_empty() { + return Err(anyhow!("Empty contract address or tag")); + } + let entrypoint = parts[1].trim(); if entrypoint.is_empty() { return Err(anyhow!("Empty entrypoint")); } + if entrypoint.len() > 31 { + return Err(anyhow!("Entrypoint name too long. Maximum length is 31 characters")); + } if !entrypoint.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { return Err(anyhow!("Invalid entrypoint format. Must contain only alphanumeric characters and underscores")); }
103-103
: Improve error message specificity.Ohayo, sensei! The error message could be more helpful by including the actual reason why the contract wasn't found (e.g., invalid tag, network issues, etc.).
- .ok_or_else(|| anyhow!("Contract {descriptor} not found in the world diff.")) + .ok_or_else(|| anyhow!("Contract {descriptor} not found. Please ensure the tag exists in the manifest or the contract is deployed if using an address."))
144-176
: Consider adding safety limits for multicalls.Ohayo, sensei! While the multicall implementation works, it might be good to add some safety measures:
- Maximum number of calls in a batch
- Maximum total calldata size
- Gas estimation before execution
Consider adding configuration options for these limits and implementing checks like:
const MAX_CALLS_PER_BATCH: usize = 20; const MAX_TOTAL_CALLDATA_SIZE: usize = 1024 * 1024; // 1MB if call_args_list.len() > MAX_CALLS_PER_BATCH { return Err(anyhow!("Too many calls in batch. Maximum is {}", MAX_CALLS_PER_BATCH)); }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
bin/sozo/src/commands/execute.rs
(4 hunks)
🔇 Additional comments (1)
bin/sozo/src/commands/execute.rs (1)
178-178
:
Fix undefined variable in invoke call.
Ohayo, sensei! The invoke
method is using an undefined call
variable. This should be removed as the calls are already added to the invoker.
- let tx_result = invoker.invoke(call).await?; // Invoking the multi-call
+ let tx_result = invoker.invoke().await?; // Invoking the multi-call
Likely invalid or redundant comment.
bin/sozo/src/commands/execute.rs
Outdated
if let Some(local_address) = ws.get_contract_address(&descriptor) { | ||
local_address | ||
} else { | ||
resolve_contract_address(&descriptor, &self, &ws).await?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect argument in resolve_contract_address call.
Ohayo, sensei! There's a bug in the resolve_contract_address call. It's passing &self
instead of &world_diff
.
- resolve_contract_address(&descriptor, &self, &ws).await?;
+ resolve_contract_address(&descriptor, &world_diff, &self, &ws).await?;
Committable suggestion skipped: line range outside the PR's diff.
Hey @glihm , I have done some fmt and also removed some errors in build process |
Let me know if there are any more stuff to be done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Outside diff range and nitpick comments (2)
bin/sozo/src/commands/execute.rs (2)
26-28
: LGTM! Consider enhancing the help text.Ohayo, sensei! The help text is clear, but we could make it even more helpful by adding examples for different types of arguments (strings, numbers, addresses).
- help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,... (ex: dojo_examples:actions,execute,1,2)" + help = "List of calls to execute. Each call should be in format: <CONTRACT_ADDRESS/TAG>,<ENTRYPOINT>,<ARG1>,<ARG2>,... (ex: dojo_examples:actions,execute,1,2 or my_contract,set_value,\"hello world\",0x1234)"
163-167
: Enhance error handling for calldata decoding.Ohayo, sensei! Consider adding more context to the error when calldata decoding fails.
let calldata = if let Some(cd) = call_args.calldata { - calldata_decoder::decode_calldata(&cd)? + calldata_decoder::decode_calldata(&cd).map_err(|e| { + anyhow!( + "Failed to decode calldata for contract {:?}, entrypoint {}: {}", + descriptor, + call_args.entrypoint, + e + ) + })? } else { vec![] };
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
bin/sozo/src/commands/execute.rs
(4 hunks)
🔇 Additional comments (1)
bin/sozo/src/commands/execute.rs (1)
178-178
:
Fix undefined variable in invoke call.
Ohayo, sensei! The invoke method is using an undefined variable 'call'.
- let tx_result = invoker.invoke(call).await?; // Invoking the multi-call
+ let tx_result = invoker.invoke().await?; // Invoking the multi-call
Likely invalid or redundant comment.
resolve_contract_address(&descriptor, &world_diff, &ws).await?; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix incorrect arguments in resolve_contract_address call.
Ohayo, sensei! The resolve_contract_address call is missing the options argument.
- resolve_contract_address(&descriptor, &world_diff, &ws).await?;
+ resolve_contract_address(&descriptor, &world_diff, &self, &ws).await?;
📝 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.
resolve_contract_address(&descriptor, &world_diff, &ws).await?; | |
}; | |
resolve_contract_address(&descriptor, &world_diff, &self, &ws).await?; | |
}; |
ResourceDescriptor::Name(_) => { | ||
unimplemented!("Expected to be a resolved tag with default namespace.") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implement the Name variant resolution.
Ohayo, sensei! The Name variant is currently unimplemented, which could cause runtime panics.
- ResourceDescriptor::Name(_) => {
- unimplemented!("Expected to be a resolved tag with default namespace.")
- }
+ ResourceDescriptor::Name(name) => {
+ let tag = format!("{}:{}", profile_config.namespace.default, name);
+ resolve_contract_address(
+ &ResourceDescriptor::Tag(tag),
+ world_diff,
+ options,
+ ws
+ ).await
+ }
Committable suggestion skipped: line range outside the PR's diff.
impl std::str::FromStr for CallArgs { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let s = s.trim(); | ||
|
||
let parts: Vec<&str> = s.split(',').collect(); | ||
if parts.len() < 2 { | ||
return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); | ||
} | ||
|
||
let entrypoint = parts[1].trim(); | ||
if entrypoint.is_empty() { | ||
return Err(anyhow!("Empty entrypoint")); | ||
} | ||
if !entrypoint.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { | ||
return Err(anyhow!("Invalid entrypoint format. Must contain only alphanumeric characters and underscores")); | ||
} | ||
|
||
Ok(CallArgs { | ||
tag_or_address: parts[0].parse()?, | ||
entrypoint: entrypoint.to_string(), | ||
calldata: if parts.len() > 2 { Some(parts[2..].join(",")) } else { None }, | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance the parsing logic for better robustness.
Ohayo, sensei! The current parsing implementation has some potential issues:
- It doesn't handle escaped commas in arguments
- Missing validation for empty tag/address
- Individual parts after split aren't trimmed
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
+ if s.is_empty() {
+ return Err(anyhow!("Empty call string"));
+ }
- let parts: Vec<&str> = s.split(',').collect();
+ // Handle escaped commas
+ let mut parts = Vec::new();
+ let mut current = String::new();
+ let mut escaped = false;
+
+ for c in s.chars() {
+ match (c, escaped) {
+ ('\\', false) => escaped = true,
+ (',', false) => {
+ parts.push(current.trim().to_string());
+ current.clear();
+ },
+ (c, _) => {
+ current.push(c);
+ escaped = false;
+ }
+ }
+ }
+ parts.push(current.trim().to_string());
+
if parts.len() < 2 {
return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,..."));
}
- let entrypoint = parts[1].trim();
+ let tag_or_address = parts[0].trim();
+ if tag_or_address.is_empty() {
+ return Err(anyhow!("Empty contract address/tag"));
+ }
+
+ let entrypoint = parts[1].trim();
if entrypoint.is_empty() {
return Err(anyhow!("Empty entrypoint"));
}
📝 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.
impl std::str::FromStr for CallArgs { | |
type Err = anyhow::Error; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
let s = s.trim(); | |
let parts: Vec<&str> = s.split(',').collect(); | |
if parts.len() < 2 { | |
return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); | |
} | |
let entrypoint = parts[1].trim(); | |
if entrypoint.is_empty() { | |
return Err(anyhow!("Empty entrypoint")); | |
} | |
if !entrypoint.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { | |
return Err(anyhow!("Invalid entrypoint format. Must contain only alphanumeric characters and underscores")); | |
} | |
Ok(CallArgs { | |
tag_or_address: parts[0].parse()?, | |
entrypoint: entrypoint.to_string(), | |
calldata: if parts.len() > 2 { Some(parts[2..].join(",")) } else { None }, | |
}) | |
} | |
} | |
impl std::str::FromStr for CallArgs { | |
type Err = anyhow::Error; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
let s = s.trim(); | |
if s.is_empty() { | |
return Err(anyhow!("Empty call string")); | |
} | |
// Handle escaped commas | |
let mut parts = Vec::new(); | |
let mut current = String::new(); | |
let mut escaped = false; | |
for c in s.chars() { | |
match (c, escaped) { | |
('\\', false) => escaped = true, | |
(',', false) => { | |
parts.push(current.trim().to_string()); | |
current.clear(); | |
}, | |
(c, _) => { | |
current.push(c); | |
escaped = false; | |
} | |
} | |
} | |
parts.push(current.trim().to_string()); | |
if parts.len() < 2 { | |
return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); | |
} | |
let tag_or_address = parts[0].trim(); | |
if tag_or_address.is_empty() { | |
return Err(anyhow!("Empty contract address/tag")); | |
} | |
let entrypoint = parts[1].trim(); | |
if entrypoint.is_empty() { | |
return Err(anyhow!("Empty entrypoint")); | |
} | |
if !entrypoint.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { | |
return Err(anyhow!("Invalid entrypoint format. Must contain only alphanumeric characters and underscores")); | |
} | |
Ok(CallArgs { | |
tag_or_address: parts[0].parse()?, | |
entrypoint: entrypoint.to_string(), | |
calldata: if parts.len() > 2 { Some(parts[2..].join(",")) } else { None }, | |
}) | |
} | |
} |
impl std::str::FromStr for CallArgs { | ||
type Err = anyhow::Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let s = s.trim(); | ||
|
||
let parts: Vec<&str> = s.split(',').collect(); | ||
if parts.len() < 2 { | ||
return Err(anyhow!("Invalid call format. Expected format: <CONTRACT_NAME>,<ENTRYPOINT_NAME>,<ARG1>,<ARG2>,...")); | ||
} | ||
|
||
let entrypoint = parts[1].trim(); | ||
if entrypoint.is_empty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please write a unit test for this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @kariy, can you tell me the steps to write a unit test for this function? I am kinda new to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @glihm , I am actually kinda getting an error running the test
This errors are like this:
Compiling zstd v0.13.2 Compiling nextest-runner v0.65.0 Finished
releaseprofile [optimized] target(s) in 2m 27s Installing /home/manush2005/.cargo/bin/cargo-nextest Installed package
cargo-nextest v0.9.82(executable
cargo-nextest) manush2005@Manush:/mnt/c/Users/Manush/OneDrive/Desktop/Manush/dojo$ cargo nextest run --all-features -p sozo Updating crates.io index Updating git repository
https://github.com/dojoengine/dojo`
error: failed to get dojo-lang
as a dependency of package scarb v2.8.4 (https://github.com/dojoengine/scarb?rev=7eac49b3e61236ce466e712225d9c989f9db1ef3#7eac49b3)
... which satisfies git dependency scarb
(locked to 2.8.4) of package dojo-test-utils v1.0.0 (/mnt/c/Users/Manush/OneDrive/Desktop/Manush/dojo/crates/dojo/test-utils)
... which satisfies path dependency dojo-test-utils
(locked to 1.0.0) of package dojo-utils v1.0.0 (/mnt/c/Users/Manush/OneDrive/Desktop/Manush/dojo/crates/dojo/utils)
... which satisfies path dependency dojo-utils
(locked to 1.0.0) of package katana v1.0.0 (/mnt/c/Users/Manush/OneDrive/Desktop/Manush/dojo/bin/katana)
Caused by:
failed to load source for dependency dojo-lang
Caused by:
Unable to update https://github.com/dojoengine/dojo?rev=6725a8f20af56213fa7382aa1e158817f3ee623c#6725a8f2
Caused by:
failed to fetch into: /home/manush2005/.cargo/git/db/dojo-10cac2e09298cf35
Caused by:
network failure seems to have happened
if a proxy or similar is necessary net.git-fetch-with-cli
may help here
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
Caused by:
SSL error: unknown error; class=Ssl (16)`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Manush-2005 how are you trying to compile the code locally?
You should follow the CONTRIBUTING.md
guide to help you out.
If you have any question, you're welcome in the dojo discord and happy to have a call to show you the flow if you still have complications to build.
Do you have some updates here @Manush-2005? |
Hey, @glihm , I am so sorry for my incovience.My university exams are going on.So I am busy with that this days. I will look into this PR |
No pressure, wanted to know if you were ok to continue or not, but seems yes. :) Sounds good, best of luck on your exams and thank you for the update. 👍 |
Any update @Manush-2005? Let me know if you don't have the bandwidth, will wrap this to have the multi call available. 👍 |
Hey I am so sorry.But my exams are going on.So I will not be able to continue forward with this issue. |
Description
This PR will ad the Sozo mutiple call feature. This PR is related to #2620 which is closed
Related issue
This is second PR of the issue #2620
Summary by CodeRabbit
New Features
Bug Fixes