From 096abf02af7d771aaeb88f9d98d3a46727dc74e6 Mon Sep 17 00:00:00 2001 From: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:41:27 +0200 Subject: [PATCH] chore: upgrade to `188c4f8` and remove temporary workarounds (#55) * upgrade to 188c4f8, which includes https://github.com/alloy-rs/alloy/commit/3faa1fc10949602ccba946686167ef66774a27f1 * use with_recommended_fillers(), remove manual gas estimation and nonce management * use deploy method on sol! artifact * clean up redundant artifacts * greatly simplify contract interaction * make test not depend on exact amounts, show common before/after balance check * simplify contract interaction --- Cargo.toml | 2 +- .../anvil/examples/deploy_contract_anvil.rs | 9 +- examples/contracts/examples/abi/Counter.json | 35 -- .../examples/deploy_from_artifact.rs | 31 +- .../examples/deploy_from_contract.rs | 33 +- .../examples/query_contract_storage.rs | 4 +- .../ERC20Example.json | 0 .../contracts/token/ERC20/ERC20.sol | 360 ------------------ .../contracts/token/ERC20/IERC20.sol | 78 ---- .../token/ERC20/extensions/IERC20Metadata.sol | 28 -- .../@openzeppelin/contracts/utils/Context.sol | 24 -- .../contracts/erc20_example/ERC20Example.sol | 10 - .../transactions/examples/transfer_erc20.rs | 92 +---- 13 files changed, 44 insertions(+), 662 deletions(-) delete mode 100644 examples/contracts/examples/abi/Counter.json rename examples/transactions/examples/{contracts => artifacts}/ERC20Example.json (100%) delete mode 100644 examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/ERC20.sol delete mode 100644 examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/IERC20.sol delete mode 100644 examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol delete mode 100644 examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/utils/Context.sol delete mode 100644 examples/transactions/examples/contracts/erc20_example/ERC20Example.sol diff --git a/Cargo.toml b/Cargo.toml index 8f47cf75..a8d2dba5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ unnecessary_struct_initialization = "allow" use_self = "allow" [workspace.dependencies] -alloy = { git = "https://github.com/alloy-rs/alloy", rev = "44c905d", features = [ +alloy = { git = "https://github.com/alloy-rs/alloy", rev = "188c4f8", features = [ "contract", "network", "node-bindings", diff --git a/examples/anvil/examples/deploy_contract_anvil.rs b/examples/anvil/examples/deploy_contract_anvil.rs index 60479790..257a1d4f 100644 --- a/examples/anvil/examples/deploy_contract_anvil.rs +++ b/examples/anvil/examples/deploy_contract_anvil.rs @@ -32,7 +32,6 @@ async fn main() -> Result<()> { // Set up signer from the first default Anvil account (Alice). let signer: LocalWallet = anvil.keys()[0].clone().into(); - let from = signer.address(); // Create a provider with a signer and the network. let rpc_url = anvil.endpoint().parse()?; @@ -44,20 +43,18 @@ async fn main() -> Result<()> { println!("Anvil running at `{}`", anvil.endpoint()); // Deploy the contract. - let contract_builder = Counter::deploy_builder(&provider).from(from); - let contract_address = contract_builder.deploy().await?; - let contract = Counter::new(contract_address, provider); + let contract = Counter::deploy(&provider).await?; println!("Deployed contract at address: {:?}", contract.address()); // Set the number to 42. - let builder = contract.setNumber(U256::from(42)).from(from); + let builder = contract.setNumber(U256::from(42)); let receipt = builder.send().await?.get_receipt().await?; println!("Set number to 42: {:?}", receipt.transaction_hash); // Increment the number to 43. - let builder = contract.increment().from(from); + let builder = contract.increment(); let receipt = builder.send().await?.get_receipt().await?; println!("Incremented number: {:?}", receipt.transaction_hash); diff --git a/examples/contracts/examples/abi/Counter.json b/examples/contracts/examples/abi/Counter.json deleted file mode 100644 index b6c39c5f..00000000 --- a/examples/contracts/examples/abi/Counter.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "type": "function", - "name": "increment", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "number", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "setNumber", - "inputs": [ - { - "name": "newNumber", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - } -] \ No newline at end of file diff --git a/examples/contracts/examples/deploy_from_artifact.rs b/examples/contracts/examples/deploy_from_artifact.rs index fea47b60..2bbc6668 100644 --- a/examples/contracts/examples/deploy_from_artifact.rs +++ b/examples/contracts/examples/deploy_from_artifact.rs @@ -1,12 +1,8 @@ //! Example of deploying a contract from an artifact to Anvil and interacting with it. use alloy::{ - network::EthereumSigner, - node_bindings::Anvil, - primitives::U256, - providers::{Provider, ProviderBuilder}, - signers::wallet::LocalWallet, - sol, + network::EthereumSigner, node_bindings::Anvil, primitives::U256, providers::ProviderBuilder, + signers::wallet::LocalWallet, sol, }; use eyre::Result; @@ -29,32 +25,25 @@ async fn main() -> Result<()> { // Create a provider with a signer. let rpc_url = anvil.endpoint().parse()?; - let provider = ProviderBuilder::new().signer(EthereumSigner::from(signer)).on_http(rpc_url)?; + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .signer(EthereumSigner::from(signer)) + .on_http(rpc_url)?; println!("Anvil running at `{}`", anvil.endpoint()); - // Get the base fee for the block. - let base_fee = provider.get_gas_price().await?; - // Deploy the contract. - let contract_builder = Counter::deploy_builder(&provider); - let estimate = contract_builder.estimate_gas().await?; - let contract_address = - contract_builder.gas(estimate).gas_price(base_fee).nonce(0).deploy().await?; - - println!("Deployed contract at address: {contract_address:?}"); + let contract = Counter::deploy(&provider).await?; - let contract = Counter::new(contract_address, &provider); + println!("Deployed contract at address: {:?}", contract.address()); - let estimate = contract.setNumber(U256::from(42)).estimate_gas().await?; - let builder = contract.setNumber(U256::from(42)).nonce(1).gas(estimate).gas_price(base_fee); + let builder = contract.setNumber(U256::from(42)); let receipt = builder.send().await?.get_receipt().await?; println!("Set number to 42: {:?}", receipt.transaction_hash); // Increment the number to 43. - let estimate = contract.increment().estimate_gas().await?; - let builder = contract.increment().nonce(2).gas(estimate).gas_price(base_fee); + let builder = contract.increment(); let receipt = builder.send().await?.get_receipt().await?; println!("Incremented number: {:?}", receipt.transaction_hash); diff --git a/examples/contracts/examples/deploy_from_contract.rs b/examples/contracts/examples/deploy_from_contract.rs index 536f6864..36526ee4 100644 --- a/examples/contracts/examples/deploy_from_contract.rs +++ b/examples/contracts/examples/deploy_from_contract.rs @@ -1,12 +1,8 @@ //! Example of deploying a contract from Solidity code to Anvil and interacting with it. use alloy::{ - network::EthereumSigner, - node_bindings::Anvil, - primitives::U256, - providers::{Provider, ProviderBuilder}, - signers::wallet::LocalWallet, - sol, + network::EthereumSigner, node_bindings::Anvil, primitives::U256, providers::ProviderBuilder, + signers::wallet::LocalWallet, sol, }; use eyre::Result; @@ -37,34 +33,29 @@ async fn main() -> Result<()> { // Set up signer from the first default Anvil account (Alice). let signer: LocalWallet = anvil.keys()[0].clone().into(); + println!("Anvil running at `{}`", anvil.endpoint()); + // Create a provider with a signer. let rpc_url = anvil.endpoint().parse()?; - let provider = ProviderBuilder::new().signer(EthereumSigner::from(signer)).on_http(rpc_url)?; + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .signer(EthereumSigner::from(signer)) + .on_http(rpc_url)?; println!("Anvil running at `{}`", anvil.endpoint()); - // Get the base fee for the block. - let base_fee = provider.get_gas_price().await?; - // Deploy the contract. - let contract_builder = Counter::deploy_builder(&provider); - let estimate = contract_builder.estimate_gas().await?; - let contract_address = - contract_builder.gas(estimate).gas_price(base_fee).nonce(0).deploy().await?; - - println!("Deployed contract at address: {contract_address:?}"); + let contract = Counter::deploy(&provider).await?; - let contract = Counter::new(contract_address, &provider); + println!("Deployed contract at address: {:?}", contract.address()); - let estimate = contract.setNumber(U256::from(42)).estimate_gas().await?; - let builder = contract.setNumber(U256::from(42)).nonce(1).gas(estimate).gas_price(base_fee); + let builder = contract.setNumber(U256::from(42)); let receipt = builder.send().await?.get_receipt().await?; println!("Set number to 42: {:?}", receipt.transaction_hash); // Increment the number to 43. - let estimate = contract.increment().estimate_gas().await?; - let builder = contract.increment().nonce(2).gas(estimate).gas_price(base_fee); + let builder = contract.increment(); let receipt = builder.send().await?.get_receipt().await?; println!("Incremented number: {:?}", receipt.transaction_hash); diff --git a/examples/queries/examples/query_contract_storage.rs b/examples/queries/examples/query_contract_storage.rs index 817e692d..4b4a9736 100644 --- a/examples/queries/examples/query_contract_storage.rs +++ b/examples/queries/examples/query_contract_storage.rs @@ -10,8 +10,8 @@ use eyre::Result; #[tokio::main] async fn main() -> Result<()> { // Create a provider. - let rpc_url = "https://eth.merkle.io"; - let provider = ProviderBuilder::new().on_builtin(rpc_url).await?; + let rpc_url = "https://eth.merkle.io".parse()?; + let provider = ProviderBuilder::new().on_http(rpc_url)?; // Get storage slot 0 from the Uniswap V3 USDC-ETH pool on Ethereum mainnet. let pool_address = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"); diff --git a/examples/transactions/examples/contracts/ERC20Example.json b/examples/transactions/examples/artifacts/ERC20Example.json similarity index 100% rename from examples/transactions/examples/contracts/ERC20Example.json rename to examples/transactions/examples/artifacts/ERC20Example.json diff --git a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/ERC20.sol b/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/ERC20.sol deleted file mode 100644 index e8d46cfc..00000000 --- a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/ERC20.sol +++ /dev/null @@ -1,360 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.0; - -import "./IERC20.sol"; -import "./extensions/IERC20Metadata.sol"; -import "../../utils/Context.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20PresetMinterPauser}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC20 - * applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract ERC20 is Context, IERC20, IERC20Metadata { - mapping(address => uint256) private _balances; - - mapping(address => mapping(address => uint256)) private _allowances; - - uint256 private _totalSupply; - - string private _name; - string private _symbol; - - /** - * @dev Sets the values for {name} and {symbol}. - * - * The default value of {decimals} is 18. To select a different value for - * {decimals} you should overload it. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual override returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the value {ERC20} uses, unless this function is - * overridden; - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual override returns (uint8) { - return 18; - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual override returns (uint256) { - return _totalSupply; - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual override returns (uint256) { - return _balances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - */ - function transfer(address to, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _transfer(owner, to, amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address owner, address spender) public view virtual override returns (uint256) { - return _allowances[owner][spender]; - } - - /** - * @dev See {IERC20-approve}. - * - * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address spender, uint256 amount) public virtual override returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - * - the caller must have allowance for ``from``'s tokens of at least - * `amount`. - */ - function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { - address spender = _msgSender(); - _spendAllowance(from, spender, amount); - _transfer(from, to, amount); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { - address owner = _msgSender(); - _approve(owner, spender, allowance(owner, spender) + addedValue); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { - address owner = _msgSender(); - uint256 currentAllowance = allowance(owner, spender); - require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); - unchecked { - _approve(owner, spender, currentAllowance - subtractedValue); - } - - return true; - } - - /** - * @dev Moves `amount` of tokens from `from` to `to`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * Requirements: - * - * - `from` cannot be the zero address. - * - `to` cannot be the zero address. - * - `from` must have a balance of at least `amount`. - */ - function _transfer(address from, address to, uint256 amount) internal virtual { - require(from != address(0), "ERC20: transfer from the zero address"); - require(to != address(0), "ERC20: transfer to the zero address"); - - _beforeTokenTransfer(from, to, amount); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _balances[to] += amount; - - emit Transfer(from, to, amount); - - _afterTokenTransfer(from, to, amount); - } - - /** - * @dev Creates `amount` tokens and assigns them to `account`, increasing - * the total supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - */ - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _beforeTokenTransfer(address(0), account, amount); - - _totalSupply += amount; - _balances[account] += amount; - emit Transfer(address(0), account, amount); - - _afterTokenTransfer(address(0), account, amount); - } - - /** - * @dev Destroys `amount` tokens from `account`, reducing the - * total supply. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * Requirements: - * - * - `account` cannot be the zero address. - * - `account` must have at least `amount` tokens. - */ - function _burn(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: burn from the zero address"); - - _beforeTokenTransfer(account, address(0), amount); - - uint256 accountBalance = _balances[account]; - require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); - unchecked { - _balances[account] = accountBalance - amount; - } - _totalSupply -= amount; - - emit Transfer(account, address(0), amount); - - _afterTokenTransfer(account, address(0), amount); - } - - /** - * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. - * - * This internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - emit Approval(owner, spender, amount); - } - - /** - * @dev Updates `owner` s allowance for `spender` based on spent `amount`. - * - * Does not update the allowance amount in case of infinite allowance. - * Revert if not enough allowance is available. - * - * Might emit an {Approval} event. - */ - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC20: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } - - /** - * @dev Hook that is called before any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * will be transferred to `to`. - * - when `from` is zero, `amount` tokens will be minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens will be burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} - - /** - * @dev Hook that is called after any transfer of tokens. This includes - * minting and burning. - * - * Calling conditions: - * - * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens - * has been transferred to `to`. - * - when `from` is zero, `amount` tokens have been minted for `to`. - * - when `to` is zero, `amount` of ``from``'s tokens have been burned. - * - `from` and `to` are never both zero. - * - * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. - */ - function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} -} diff --git a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/IERC20.sol b/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/IERC20.sol deleted file mode 100644 index 66c4e4d8..00000000 --- a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/IERC20.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. - */ -interface IERC20 { - /** - * @dev Emitted when `value` tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); - - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval(address indexed owner, address indexed spender, uint256 value); - - /** - * @dev Returns the amount of tokens in existence. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns the amount of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev Moves `amount` tokens from the caller's account to `to`. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 amount) external returns (bool); - - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - */ - function allowance(address owner, address spender) external view returns (uint256); - - /** - * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 amount) external returns (bool); - - /** - * @dev Moves `amount` tokens from `from` to `to` using the - * allowance mechanism. `amount` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom(address from, address to, uint256 amount) external returns (bool); -} diff --git a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol b/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol deleted file mode 100644 index 83ba6ac5..00000000 --- a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) - -pragma solidity ^0.8.0; - -import "../IERC20.sol"; - -/** - * @dev Interface for the optional metadata functions from the ERC20 standard. - * - * _Available since v4.1._ - */ -interface IERC20Metadata is IERC20 { - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); -} diff --git a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/utils/Context.sol b/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/utils/Context.sol deleted file mode 100644 index f304065b..00000000 --- a/examples/transactions/examples/contracts/erc20_example/@openzeppelin/contracts/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} diff --git a/examples/transactions/examples/contracts/erc20_example/ERC20Example.sol b/examples/transactions/examples/contracts/erc20_example/ERC20Example.sol deleted file mode 100644 index e2660ac3..00000000 --- a/examples/transactions/examples/contracts/erc20_example/ERC20Example.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract ERC20Example is ERC20 { - constructor() ERC20("ERC20Example", "XYZ") { - _mint(msg.sender, 1000 * 10 ** decimals()); - } -} diff --git a/examples/transactions/examples/transfer_erc20.rs b/examples/transactions/examples/transfer_erc20.rs index 4b9a464c..d4e10c00 100644 --- a/examples/transactions/examples/transfer_erc20.rs +++ b/examples/transactions/examples/transfer_erc20.rs @@ -1,21 +1,14 @@ //! Example of how to transfer ERC20 tokens from one account to another. -use alloy::{ - network::{Ethereum, TransactionBuilder}, - node_bindings::Anvil, - primitives::{Address, Bytes, U256}, - providers::{Provider, ProviderBuilder, ReqwestProvider}, - rpc::types::eth::{BlockId, TransactionRequest}, - sol, - sol_types::SolCall, -}; +use alloy::{node_bindings::Anvil, primitives::U256, providers::ProviderBuilder, sol}; use eyre::Result; // Codegen from artifact. sol!( #[allow(missing_docs)] + #[sol(rpc)] ERC20Example, - "examples/contracts/ERC20Example.json" + "examples/artifacts/ERC20Example.json" ); #[tokio::main] @@ -33,78 +26,25 @@ async fn main() -> Result<()> { let bob = anvil.addresses()[1]; // Deploy the `ERC20Example` contract. - let contract_address = deploy_token_contract(&provider, alice).await?; + let contract = ERC20Example::deploy(provider).await?; - // Create the transaction input to transfer 100 tokens from Alice to Bob. - let input = ERC20Example::transferCall { to: bob, amount: U256::from(100) }.abi_encode(); - let input = Bytes::from(input); + // Register the balances of Alice and Bob before the transfer. + let alice_before_balance = contract.balanceOf(alice).call().await?._0; + let bob_before_balance = contract.balanceOf(bob).call().await?._0; - // Create a transaction with the input. - let tx = TransactionRequest { - from: Some(alice), - to: Some(contract_address), - input: Some(input).into(), - ..Default::default() - }; - - // Send the transaction and wait for the receipt. - let receipt = provider.send_transaction(tx).await?.get_receipt().await?; + // Transfer and wait for the receipt. + let amount = U256::from(100); + let receipt = contract.transfer(bob, amount).send().await?.get_receipt().await?; println!("Send transaction: {:?}", receipt.transaction_hash); - // Check the balances of Alice and Bob after the transfer. - let alice_balance = balance_of(&provider, alice, contract_address).await?; - let bob_balance = balance_of(&provider, bob, contract_address).await?; + // Register the balances of Alice and Bob after the transfer. + let alice_after_balance = contract.balanceOf(alice).call().await?._0; + let bob_after_balance = contract.balanceOf(bob).call().await?._0; - assert_eq!(alice_balance, U256::from(999999999999999999900_i128)); - assert_eq!(bob_balance, U256::from(100)); + // Check the balances of Alice and Bob after the transfer. + assert_eq!(alice_before_balance - alice_after_balance, amount); + assert_eq!(bob_after_balance - bob_before_balance, amount); Ok(()) } - -async fn deploy_token_contract( - provider: &ReqwestProvider, - from: Address, -) -> Result
{ - // Compile the contract. - let bytecode = ERC20Example::BYTECODE.to_owned(); - - // Create a transaction. - let tx = TransactionRequest { - from: Some(from), - input: Some(bytecode).into(), - to: None, - ..Default::default() - }; - - // Send the transaction and wait for the receipt. - let receipt = provider.send_transaction(tx).await?.get_receipt().await?; - - // Get the contract address. - let contract_address = receipt.contract_address.expect("Contract address not found"); - - println!("Deployed contract at: {contract_address}"); - - Ok(contract_address) -} - -async fn balance_of( - provider: &ReqwestProvider, - account: Address, - contract_address: Address, -) -> Result { - // Encode the call. - let call = ERC20Example::balanceOfCall { account }.abi_encode(); - let input = Bytes::from(call); - - // Build a transaction to call the contract with the input. - let tx = TransactionRequest::default().with_to(contract_address.into()).with_input(input); - - // Call the contract. - let result = provider.call(&tx, BlockId::latest()).await?; - - // Decode the result. - let result = ERC20Example::balanceOfCall::abi_decode_returns(&result, true)?._0; - - Ok(result) -}