mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Work on testing the Router
This commit is contained in:
28
processor/ethereum/test-primitives/Cargo.toml
Normal file
28
processor/ethereum/test-primitives/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "serai-ethereum-test-primitives"
|
||||
version = "0.1.0"
|
||||
description = "Test primitives for Ethereum"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/processor/ethereum/test-primitives"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
k256 = { version = "0.13", default-features = false, features = ["std"] }
|
||||
|
||||
alloy-core = { version = "0.8", default-features = false }
|
||||
alloy-consensus = { version = "0.3", default-features = false, features = ["std"] }
|
||||
|
||||
alloy-rpc-types-eth = { version = "0.3", default-features = false }
|
||||
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||
alloy-provider = { version = "0.3", default-features = false }
|
||||
|
||||
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
||||
15
processor/ethereum/test-primitives/LICENSE
Normal file
15
processor/ethereum/test-primitives/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2022-2024 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
5
processor/ethereum/test-primitives/README.md
Normal file
5
processor/ethereum/test-primitives/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Ethereum Router
|
||||
|
||||
The [Router contract](./contracts/Router.sol) is extensively documented to ensure clarity and
|
||||
understanding of the design decisions made. Please refer to it for understanding of why/what this
|
||||
is.
|
||||
117
processor/ethereum/test-primitives/src/lib.rs
Normal file
117
processor/ethereum/test-primitives/src/lib.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use k256::{elliptic_curve::sec1::ToEncodedPoint, ProjectivePoint};
|
||||
|
||||
use alloy_core::{
|
||||
primitives::{Address, U256, Bytes, Signature, TxKind},
|
||||
hex::FromHex,
|
||||
};
|
||||
use alloy_consensus::{SignableTransaction, TxLegacy, Signed};
|
||||
|
||||
use alloy_rpc_types_eth::TransactionReceipt;
|
||||
use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
use ethereum_primitives::{keccak256, deterministically_sign};
|
||||
|
||||
fn address(point: &ProjectivePoint) -> [u8; 20] {
|
||||
let encoded_point = point.to_encoded_point(false);
|
||||
// Last 20 bytes of the hash of the concatenated x and y coordinates
|
||||
// We obtain the concatenated x and y coordinates via the uncompressed encoding of the point
|
||||
keccak256(&encoded_point.as_ref()[1 .. 65])[12 ..].try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Fund an account.
|
||||
pub async fn fund_account(provider: &RootProvider<SimpleRequest>, address: Address, value: U256) {
|
||||
let _: () = provider
|
||||
.raw_request("anvil_setBalance".into(), [address.to_string(), value.to_string()])
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Publish an already-signed transaction.
|
||||
pub async fn publish_tx(
|
||||
provider: &RootProvider<SimpleRequest>,
|
||||
tx: Signed<TxLegacy>,
|
||||
) -> TransactionReceipt {
|
||||
// Fund the sender's address
|
||||
fund_account(
|
||||
provider,
|
||||
tx.recover_signer().unwrap(),
|
||||
(U256::from(tx.tx().gas_limit) * U256::from(tx.tx().gas_price)) + tx.tx().value,
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, sig, _) = tx.into_parts();
|
||||
let mut bytes = vec![];
|
||||
tx.encode_with_signature_fields(&sig, &mut bytes);
|
||||
let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap();
|
||||
pending_tx.get_receipt().await.unwrap()
|
||||
}
|
||||
|
||||
/// Deploy a contract.
|
||||
///
|
||||
/// The contract deployment will be done by a random account.
|
||||
pub async fn deploy_contract(
|
||||
provider: &RootProvider<SimpleRequest>,
|
||||
file_path: &str,
|
||||
constructor_arguments: &[u8],
|
||||
) -> Address {
|
||||
let hex_bin_buf = std::fs::read_to_string(file_path).unwrap();
|
||||
let hex_bin =
|
||||
if let Some(stripped) = hex_bin_buf.strip_prefix("0x") { stripped } else { &hex_bin_buf };
|
||||
let mut bin = Vec::<u8>::from(Bytes::from_hex(hex_bin).unwrap());
|
||||
bin.extend(constructor_arguments);
|
||||
|
||||
let deployment_tx = TxLegacy {
|
||||
chain_id: None,
|
||||
nonce: 0,
|
||||
// 100 gwei
|
||||
gas_price: 100_000_000_000u128,
|
||||
gas_limit: 1_000_000,
|
||||
to: TxKind::Create,
|
||||
value: U256::ZERO,
|
||||
input: bin.into(),
|
||||
};
|
||||
|
||||
let deployment_tx = deterministically_sign(&deployment_tx);
|
||||
|
||||
let receipt = publish_tx(provider, deployment_tx).await;
|
||||
assert!(receipt.status());
|
||||
|
||||
receipt.contract_address.unwrap()
|
||||
}
|
||||
|
||||
/// Sign and send a transaction from the specified wallet.
|
||||
///
|
||||
/// This assumes the wallet is funded.
|
||||
pub async fn send(
|
||||
provider: &RootProvider<SimpleRequest>,
|
||||
wallet: &k256::ecdsa::SigningKey,
|
||||
mut tx: TxLegacy,
|
||||
) -> TransactionReceipt {
|
||||
let verifying_key = *wallet.verifying_key().as_affine();
|
||||
let address = Address::from(address(&verifying_key.into()));
|
||||
|
||||
// https://github.com/alloy-rs/alloy/issues/539
|
||||
// let chain_id = provider.get_chain_id().await.unwrap();
|
||||
// tx.chain_id = Some(chain_id);
|
||||
tx.chain_id = None;
|
||||
tx.nonce = provider.get_transaction_count(address).await.unwrap();
|
||||
// 100 gwei
|
||||
tx.gas_price = 100_000_000_000u128;
|
||||
|
||||
let sig = wallet.sign_prehash_recoverable(tx.signature_hash().as_ref()).unwrap();
|
||||
assert_eq!(address, tx.clone().into_signed(sig.into()).recover_signer().unwrap());
|
||||
assert!(
|
||||
provider.get_balance(address).await.unwrap() >
|
||||
((U256::from(tx.gas_price) * U256::from(tx.gas_limit)) + tx.value)
|
||||
);
|
||||
|
||||
let mut bytes = vec![];
|
||||
tx.encode_with_signature_fields(&Signature::from(sig), &mut bytes);
|
||||
let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap();
|
||||
pending_tx.get_receipt().await.unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user