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

feat(contract): interact using ContractInstance #109

Merged
merged 10 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,5 @@ tokio = "1"

# misc
eyre = "0.6.12"
serde_json = "1.0.117"
const-hex = "1.12.0"
yash-atreya marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ This repository contains the following examples:
- [x] [Math operations](./examples/big-numbers/examples/math_operations.rs)
- [x] [Math utilities](./examples/big-numbers/examples/math_utilities.rs)
- [x] Contracts
- [x] [Interact using contract instance](./examples/contracts/examples/contract_instance.rs)
- [x] [Deploy from artifact](./examples/contracts/examples/deploy_from_artifact.rs)
- [x] [Deploy from bytecode](./examples/contracts/examples/deploy_from_bytecode.rs)
- [x] [Deploy from contract](./examples/contracts/examples/deploy_from_contract.rs)
Expand Down
2 changes: 2 additions & 0 deletions examples/contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ alloy.workspace = true

eyre.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
serde_json.workspace = true
const-hex.workspace = true
94 changes: 94 additions & 0 deletions examples/contracts/examples/contract_instance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! This example demonstrates how to interact with a contract that is already deployed onchain using
//! the `ContractInstance` interface.
use alloy::{
contract::{ContractInstance, Interface},
dyn_abi::DynSolValue,
network::{Ethereum, TransactionBuilder},
primitives::U256,
providers::{Provider, ProviderBuilder},
rpc::types::TransactionRequest,
transports::http::{Client, Http},
};
use const_hex as hex;
yash-atreya marked this conversation as resolved.
Show resolved Hide resolved
use eyre::Result;

/**
* solc v0.8.26; solc a.sol --via-ir --optimize --bin
* contract Counter {
uint256 public number;

function setNumber(uint256 newNumber) public {
number = newNumber;
}

function increment() public {
number++;
}
}
*
*/
#[tokio::main]
async fn main() -> Result<()> {
let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet();

// Deploy the Counter contract
let bytecode = hex::decode("6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033")?;
let deploy_tx = TransactionRequest::default().with_deploy_code(bytecode);

let contract_address =
provider.send_transaction(deploy_tx).await?.get_receipt().await?.contract_address.unwrap();

// Get the contract abi
let path = std::env::current_dir()?.join("examples/contracts/examples/artifacts/Counter.json");

// Read the artifact which contains `abi`, `bytecode`, `deployedBytecode`,and `metadata`
let artifact = std::fs::read(path).unwrap();
let json: serde_json::Value = serde_json::from_slice(&artifact)?;

// Get `abi` from the artifact
let abi_val = json.get("abi").unwrap();
let abi = serde_json::from_str(&abi_val.to_string())?;

// Create a new `ContractInstance` of the Counter contract from the abi
let counter_instance: ContractInstance<Http<Client>, _, Ethereum> =
ContractInstance::new(contract_address, provider.clone(), Interface::new(abi));

// Interact with the contract
assert_eq!(counter_instance.abi().functions().count(), 3);

// Read
let init_val = counter_instance.function("number", &[])?.call().await?;

// Get the Uint value from the result
let init_val = init_val.first().unwrap().as_uint().unwrap().0;
Copy link
Member

Choose a reason for hiding this comment

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

Honestly this is not a great example because we know ahead of time the functions we're going to call, and the return types of the functions, which is where sol! should be used.

Copy link
Member Author

@yash-atreya yash-atreya Jun 17, 2024

Choose a reason for hiding this comment

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

I think this example just serves an introduction ContractInstance API interface. I can add another example where we call the function by the selector and match DynSolValue on the return.

Copy link
Member Author

Choose a reason for hiding this comment

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

And anyways, we still require an abi so don't we always have some idea of which function we're trying to call.

Copy link
Member

Choose a reason for hiding this comment

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

No because the ABI is supposed to be dynamic, think Foundry that executes fuzz functions with arbitrary parameters


assert_eq!(U256::from(0), init_val);

// Increment
let incr_receipt =
counter_instance.function("increment", &[])?.send().await?.get_receipt().await?;

assert!(incr_receipt.status());

let incremented_val = counter_instance.function("number", &[])?.call().await?;

let incremented_val = incremented_val.first().unwrap().as_uint().unwrap().0;

assert_eq!(U256::from(1), incremented_val);

// Set the number
let set_val = DynSolValue::from(U256::from(100));

let set_receipt =
counter_instance.function("setNumber", &[set_val])?.send().await?.get_receipt().await?;

assert!(set_receipt.status());

let set_val = counter_instance.function("number", &[])?.call().await?;

let set_val = set_val.first().unwrap().as_uint().unwrap().0;

assert_eq!(U256::from(100), set_val);

Ok(())
}
Loading