Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test endpoints for getting balance #166

Merged
merged 4 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
299 changes: 171 additions & 128 deletions crates/erc20_payment_lib/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,155 +471,198 @@ pub struct GetBalanceArgs {
pub chain_id: Option<u64>,
}

pub async fn get_balance(
async fn get_balance_using_contract_wrapper(
web3: Arc<Web3RpcPool>,
args: GetBalanceArgs,
) -> Result<GetBalanceResult, PaymentError> {
log::debug!(
"Checking balance for address {:#x}, token address: {:#x}",
args.address,
args.token_address.unwrap_or_default(),
);
token_address: Address,
call_with_details: Address,
) -> Result<Option<GetBalanceResult>, PaymentError> {
let abi_encoded_get_balance = encode_erc20_balance_of(args.address).map_err(err_from!())?;

if let (Some(token_address), Some(call_with_details)) =
(args.token_address, args.call_with_details)
let call_data =
encode_call_with_details(token_address, abi_encoded_get_balance).map_err(err_from!())?;

let block_id = if let Some(block_number) = args.block_number {
log::debug!(
"Checking balance (contract) for block number {}",
block_number
);
Some(BlockId::Number(BlockNumber::Number(block_number.into())))
} else {
log::debug!("Checking balance (contract) for latest block");
None
};
match web3
.clone()
.eth_call(
CallRequest {
from: Some(args.address),
to: Some(call_with_details),
data: Some(Bytes::from(call_data)),
..Default::default()
},
block_id,
)
.await
{
let abi_encoded_get_balance = encode_erc20_balance_of(args.address).map_err(err_from!())?;
Ok(res) => {
let (block_info, call_result) = decode_call_with_details(&res.0)?;

let call_data = encode_call_with_details(token_address, abi_encoded_get_balance)
.map_err(err_from!())?;
if let Some(chain_id) = args.chain_id {
if block_info.chain_id != chain_id {
return Err(err_custom_create!(
"Invalid chain id in response: {}, expected {}",
block_info.chain_id,
chain_id
));
}
}

let token_balance = U256::from_big_endian(&call_result);

let block_id = if let Some(block_number) = args.block_number {
log::debug!(
"Checking balance (contract) for block number {}",
block_number
"Token balance response: {:?} - token balance: {}",
block_info,
token_balance
);
Some(BlockId::Number(BlockNumber::Number(block_number.into())))
} else {
log::debug!("Checking balance (contract) for latest block");
None
};
Ok(Some(GetBalanceResult {
gas_balance: Some(block_info.eth_balance),
token_balance: Some(token_balance),
block_number: block_info.block_number,
block_datetime: block_info.block_datetime,
}))
}
Err(e) => {
if e.to_string().to_lowercase().contains("insufficient funds") {
log::warn!(
"Balance check via wrapper contract failed, falling back to standard method"
);
Ok(None)
} else {
log::error!(
"Error getting balance for account: {:#x} - {}",
args.address,
e
);
Err(err_custom_create!(
"Error getting balance for account: {:#x} - {}",
args.address,
e
))
}
}
}
}

async fn get_balance_simple(
web3: Arc<Web3RpcPool>,
args: GetBalanceArgs,
) -> Result<GetBalanceResult, PaymentError> {
let block_id = if let Some(block_number) = args.block_number {
log::debug!("Checking balance for block number {}", block_number);
BlockId::Number(BlockNumber::Number(block_number.into()))
} else {
log::debug!("Checking balance for latest block");
BlockId::Number(BlockNumber::Latest)
};
let block_info = web3
.clone()
.eth_block(block_id)
.await
.map_err(err_from!())?
.ok_or(err_custom_create!("Cannot found block_info"))?;

let block_number = block_info
.number
.ok_or(err_custom_create!(
"Failed to found block number in block info",
))?
.as_u64();
let gas_balance = Some(
web3.clone()
.eth_balance(args.address, Some(BlockNumber::Number(block_number.into())))
.await
.map_err(err_from!())?,
);

let block_number = block_info
.number
.ok_or(err_custom_create!(
"Failed to found block number in block info",
))?
.as_u64();

let block_date = datetime_from_u256_timestamp(block_info.timestamp).ok_or(
err_custom_create!("Failed to found block date in block info"),
)?;

let token_balance = if let Some(token_address) = args.token_address {
let call_data = encode_erc20_balance_of(args.address).map_err(err_from!())?;
let res = web3
.clone()
.eth_call(
CallRequest {
from: Some(args.address),
to: Some(call_with_details),
from: None,
to: Some(token_address),
gas: None,
gas_price: None,
value: None,
data: Some(Bytes::from(call_data)),
..Default::default()
transaction_type: None,
access_list: None,
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
},
block_id,
Some(BlockId::Number(BlockNumber::Number(block_number.into()))),
)
.await
.map_err(err_from!())?;
if res.0.len() != 32 {
return Err(err_create!(TransactionFailedError::new(&format!(
"Invalid balance response: {:?}. Probably not a valid ERC20 contract {:#x}",
res.0, token_address
))));
};
Some(U256::from_big_endian(&res.0))
} else {
None
};
Ok(GetBalanceResult {
gas_balance,
token_balance,
block_number,
block_datetime: block_date,
})
}

let (block_info, call_result) = decode_call_with_details(&res.0)?;

/*let now = chrono::Utc::now();
let seconds_old = (now - block_info.block_datetime).num_seconds();
if seconds_old > 10 {
log::warn!("Balance is {seconds_old}s old");
}*/
//decode call_result
if let Some(chain_id) = args.chain_id {
if block_info.chain_id != chain_id {
return Err(err_custom_create!(
"Invalid chain id in response: {}, expected {}",
block_info.chain_id,
chain_id
));
}
}

let token_balance = U256::from_big_endian(&call_result);
pub async fn get_balance(
web3: Arc<Web3RpcPool>,
args: GetBalanceArgs,
) -> Result<GetBalanceResult, PaymentError> {
log::debug!(
"Checking balance for address {:#x}, token address: {:#x}",
args.address,
args.token_address.unwrap_or_default(),
);

log::debug!(
"Token balance response: {:?} - token balance: {}",
block_info,
token_balance
);
Ok(GetBalanceResult {
gas_balance: Some(block_info.eth_balance),
token_balance: Some(token_balance),
block_number: block_info.block_number,
block_datetime: block_info.block_datetime,
})
let balance = if let (Some(token_address), Some(call_with_details)) =
(args.token_address, args.call_with_details)
{
get_balance_using_contract_wrapper(
web3.clone(),
args.clone(),
token_address,
call_with_details,
)
.await?
} else {
let block_id = if let Some(block_number) = args.block_number {
log::debug!("Checking balance for block number {}", block_number);
BlockId::Number(BlockNumber::Number(block_number.into()))
} else {
log::debug!("Checking balance for latest block");
BlockId::Number(BlockNumber::Latest)
};
let block_info = web3
.clone()
.eth_block(block_id)
.await
.map_err(err_from!())?
.ok_or(err_custom_create!("Cannot found block_info"))?;

let block_number = block_info
.number
.ok_or(err_custom_create!(
"Failed to found block number in block info",
))?
.as_u64();
let gas_balance = Some(
web3.clone()
.eth_balance(args.address, Some(BlockNumber::Number(block_number.into())))
.await
.map_err(err_from!())?,
);
None
};

let block_number = block_info
.number
.ok_or(err_custom_create!(
"Failed to found block number in block info",
))?
.as_u64();

let block_date = datetime_from_u256_timestamp(block_info.timestamp).ok_or(
err_custom_create!("Failed to found block date in block info"),
)?;

let token_balance = if let Some(token_address) = args.token_address {
let call_data = encode_erc20_balance_of(args.address).map_err(err_from!())?;
let res = web3
.clone()
.eth_call(
CallRequest {
from: None,
to: Some(token_address),
gas: None,
gas_price: None,
value: None,
data: Some(Bytes::from(call_data)),
transaction_type: None,
access_list: None,
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
},
Some(BlockId::Number(BlockNumber::Number(block_number.into()))),
)
.await
.map_err(err_from!())?;
if res.0.len() != 32 {
return Err(err_create!(TransactionFailedError::new(&format!(
"Invalid balance response: {:?}. Probably not a valid ERC20 contract {:#x}",
res.0, token_address
))));
};
Some(U256::from_big_endian(&res.0))
} else {
None
};
Ok(GetBalanceResult {
gas_balance,
token_balance,
block_number,
block_datetime: block_date,
})
if let Some(balance) = balance {
Ok(balance)
} else {
get_balance_simple(web3, args).await
}
}

Expand Down
Loading
Loading