Skip to content

Commit

Permalink
payment: add mint-only option to support payment fund on networks w/o…
Browse files Browse the repository at this point in the history
… a faucet
  • Loading branch information
kamirr committed Aug 8, 2024
1 parent 2a28d5f commit f094df4
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 91 deletions.
12 changes: 11 additions & 1 deletion core/model/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,21 @@ pub struct Fund {
address: String,
network: Option<String>,
token: Option<String>,
mint_only: bool,
}

impl Fund {
pub fn new(address: String, network: Option<String>, token: Option<String>) -> Self {
pub fn new(
address: String,
network: Option<String>,
token: Option<String>,
mint_only: bool,
) -> Self {
Self {
address,
network,
token,
mint_only,
}
}
pub fn address(&self) -> String {
Expand All @@ -235,6 +242,9 @@ impl Fund {
pub fn token(&self) -> Option<String> {
self.token.clone()
}
pub fn mint_only(&self) -> bool {
self.mint_only
}
}

impl RpcMessage for Fund {
Expand Down
217 changes: 130 additions & 87 deletions core/payment-driver/erc20/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use erc20_payment_lib::model::{DepositId, TokenTransferDbObj, TxDbObj};
use erc20_payment_lib::runtime::{
PaymentRuntime, TransferArgs, TransferType, ValidateDepositResult, VerifyTransactionResult,
};
use erc20_payment_lib::setup::FaucetSetup;
use erc20_payment_lib::signer::SignerAccount;
use erc20_payment_lib::utils::{DecimalConvExt, U256ConvExt};
use erc20_payment_lib::{DriverEvent, DriverEventContent};
Expand All @@ -26,6 +27,7 @@ use uuid::Uuid;
use web3::types::{Address, H256};
use ya_client_model::payment::allocation::Deposit;
use ya_client_model::payment::DriverStatusProperty;
use ya_payment_driver::db::models::Network;
use ya_payment_driver::driver::IdentityError;

use ya_payment_driver::{
Expand Down Expand Up @@ -577,6 +579,126 @@ impl Erc20Driver {

Ok(ValidateAllocationResult::Valid)
}

async fn fund_eth_faucet(
&self,
faucet_setup: &FaucetSetup,
network: Network,
starting_eth_balance: U256,
address: H160,
) -> Result<U256, GenericError> {
let faucet_client_max_eth_allowed =
faucet_setup
.client_max_eth_allowed
.ok_or(GenericError::new(format!(
"Missing faucet client max eth allowed for network {}",
network
)))?;

let faucet_srv_prefix =
faucet_setup
.client_srv
.as_ref()
.ok_or(GenericError::new(format!(
"Missing faucet_srv_port for network {}",
network
)))?;
let faucet_lookup_domain = faucet_setup
.lookup_domain
.as_ref()
.ok_or(GenericError::new(format!(
"Missing faucet_lookup_domain for network {}",
network
)))?;
let faucet_srv_port = faucet_setup.srv_port.ok_or(GenericError::new(format!(
"Missing faucet_srv_port for network {}",
network
)))?;
let faucet_host = faucet_setup
.client_host
.as_ref()
.ok_or(GenericError::new(format!(
"Missing faucet_host for network {}",
network
)))?;

let eth_received = if starting_eth_balance
< faucet_client_max_eth_allowed
.to_u256_from_eth()
.map_err(|err| {
GenericError::new(format!(
"faucet_client_max_eth_allowed failed to convert {}",
err
))
})? {
match faucet_donate(
faucet_srv_prefix,
faucet_lookup_domain,
faucet_host,
faucet_srv_port,
address,
)
.await
{
Ok(_) => {
log::info!("Faucet donation successful");
}
Err(e) => {
log::error!("Error donating from faucet: {}", e);
}
}
let time_now = Instant::now();
loop {
tokio::time::sleep(std::time::Duration::from_secs(5)).await;

if time_now.elapsed().as_secs() > 120 {
log::error!(
"Faucet donation not received after {} seconds",
time_now.elapsed().as_secs()
);
return Err(GenericError::new(format!(
"Faucet donation not received after {} seconds",
time_now.elapsed().as_secs()
)));
}
match self
.payment_runtime
.get_token_balance(network.to_string(), address, None)
.await
{
Ok(balance_res) => {
let current_balance = balance_res.gas_balance.unwrap_or(U256::zero());
if current_balance > starting_eth_balance {
log::info!(
"Received {} ETH from faucet",
(current_balance - starting_eth_balance).to_eth_str()
);
break current_balance - starting_eth_balance;
} else {
log::info!(
"Waiting for ETH from faucet. Current balance: {}. Elapsed: {}/{}",
current_balance.to_eth_str(),
time_now.elapsed().as_secs(),
120
);
}
}
Err(err) => {
log::error!("Error getting gas balance: {}", err);
}
}
}
} else {
log::info!(
"ETH balance is {} which is more than {} allowed by faucet",
starting_eth_balance.to_eth_str(),
faucet_client_max_eth_allowed
);
U256::zero()
};

Ok(eth_received)
}
}

#[async_trait(?Send)]
Expand Down Expand Up @@ -764,13 +886,6 @@ impl PaymentDriver for Erc20Driver {
"Missing mint min glm allowed for network {}",
network
)))?;
let faucet_client_max_eth_allowed =
faucet_setup
.client_max_eth_allowed
.ok_or(GenericError::new(format!(
"Missing faucet client max eth allowed for network {}",
network
)))?;

let (starting_eth_balance, starting_glm_balance) = match self
.payment_runtime
Expand Down Expand Up @@ -798,89 +913,17 @@ impl PaymentDriver for Erc20Driver {
}
};

let faucet_srv_prefix = faucet_setup.client_srv.ok_or(GenericError::new(format!(
"Missing faucet_srv_port for network {}",
network
)))?;
let faucet_lookup_domain = faucet_setup.lookup_domain.ok_or(GenericError::new(
format!("Missing faucet_lookup_domain for network {}", network),
))?;
let faucet_srv_port = faucet_setup.srv_port.ok_or(GenericError::new(format!(
"Missing faucet_srv_port for network {}",
network
)))?;
let faucet_host = faucet_setup.client_host.ok_or(GenericError::new(format!(
"Missing faucet_host for network {}",
network
)))?;
let faucet_configured = faucet_setup.client_host.is_some();

let eth_received = if starting_eth_balance
< faucet_client_max_eth_allowed
.to_u256_from_eth()
.map_err(|err| {
GenericError::new(format!(
"faucet_client_max_eth_allowed failed to convert {}",
err
))
})? {
match faucet_donate(
&faucet_srv_prefix,
&faucet_lookup_domain,
&faucet_host,
faucet_srv_port,
address,
)
.await
{
Ok(_) => {
log::info!("Faucet donation successful");
}
Err(e) => {
log::error!("Error donating from faucet: {}", e);
}
}
let time_now = Instant::now();
loop {
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
if !faucet_configured && !msg.mint_only() {
return Err(GenericError::new(format!("Network {} doesn't have faucet configured, so full fund (tETH + tGLM) cannot be done. \
After obtaining funds, re-run with --mint-only to obtain tGLM token.", network)));
}

if time_now.elapsed().as_secs() > 120 {
log::error!(
"Faucet donation not received after {} seconds",
time_now.elapsed().as_secs()
);
return Err(GenericError::new(format!(
"Faucet donation not received after {} seconds",
time_now.elapsed().as_secs()
)));
}
match self
.payment_runtime
.get_token_balance(network.to_string(), address, None)
.await
{
Ok(balance_res) => {
let current_balance = balance_res.gas_balance.unwrap_or(U256::zero());
if current_balance > starting_eth_balance {
log::info!(
"Received {} ETH from faucet",
(current_balance - starting_eth_balance).to_eth_str()
);
break current_balance - starting_eth_balance;
} else {
log::info!("Waiting for ETH from faucet. Current balance: {}. Elapsed: {}/{}", current_balance.to_eth_str(), time_now.elapsed().as_secs(), 120);
}
}
Err(err) => {
log::error!("Error getting gas balance: {}", err);
}
}
}
let eth_received = if faucet_configured && !msg.mint_only() {
self.fund_eth_faucet(&faucet_setup, network, starting_eth_balance, address)
.await?
} else {
log::info!(
"ETH balance is {} which is more than {} allowed by faucet",
starting_eth_balance.to_eth_str(),
faucet_client_max_eth_allowed
);
U256::zero()
};

Expand Down
14 changes: 12 additions & 2 deletions core/payment/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub enum PaymentCli {
Fund {
#[structopt(flatten)]
account: pay::AccountCli,
/// Mint token without attempting to obtain native currency from faucet
#[structopt(long = "mint-only")]
mint_only: bool,
},

/// Initialize payment account (i.e. make it ready for sending/receiving funds)
Expand Down Expand Up @@ -150,7 +153,7 @@ pub enum InvoiceCommand {
impl PaymentCli {
pub async fn run_command(self, ctx: &CliCtx) -> anyhow::Result<CommandOutput> {
match self {
PaymentCli::Fund { account } => {
PaymentCli::Fund { account, mint_only } => {
let address = resolve_address(account.address()).await?;

let onboarding_supported = matches!(account.network, NetworkName::Polygon);
Expand Down Expand Up @@ -199,7 +202,14 @@ Typically operation should take less than 1 minute.
log::warn!("{}", warn_message);

CommandOutput::object(
wallet::fund(address, account.driver(), Some(account.network()), None).await?,
wallet::fund(
address,
account.driver(),
Some(account.network()),
None,
mint_only,
)
.await?,
)
}
PaymentCli::Init {
Expand Down
3 changes: 2 additions & 1 deletion core/payment/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ pub async fn fund(
driver: String,
network: Option<String>,
token: Option<String>,
mint_only: bool,
) -> anyhow::Result<String> {
let driver_id = driver_bus_id(driver);
let message = Fund::new(address, network, token);
let message = Fund::new(address, network, token, mint_only);
let reply = bus::service(driver_id).call(message).await??;
Ok(reply)
}
Expand Down

0 comments on commit f094df4

Please sign in to comment.