Skip to content
Open
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
2 changes: 1 addition & 1 deletion core/fly_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,7 @@ void FlyClient::NetworkStd::Connection::OnMsg(ProofKernel2&& msg)
if (!req.m_Msg.m_Fetch)
ThrowUnexpected();

if (req.m_Res.m_Kernel->IsValid(req.m_Res.m_Height))
if (!req.m_Res.m_Kernel->IsValid(req.m_Res.m_Height))
ThrowUnexpected();

if (req.m_Res.m_Kernel->get_ID() != req.m_Msg.m_ID)
Expand Down
Binary file added mytest-utxo-image.bin
Binary file not shown.
Binary file added mytest.db
Binary file not shown.
Binary file added receiver_wallet.db
Binary file not shown.
Binary file added sender_wallet.db
Binary file not shown.
4 changes: 3 additions & 1 deletion utility/cli/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ namespace beam
const char* ESTIMATE_SWAP_FEERATE = "recommended_fee_rate";
const char* GET_BALANCE = "get_balance";
const char* SWAP_COIN = "swap_coin";
const char* TOKEN_CONTRACT = "token_contract";
const char* SWAP_BEAM_SIDE = "swap_beam_side";
const char* SWAP_TX_HISTORY = "swap_tx_history";
const char* NODE_POLL_PERIOD = "node_poll_period";
Expand Down Expand Up @@ -528,7 +529,8 @@ namespace beam
(cli::SWAP_WALLET_ADDR, po::value<string>(), "rpc address of the swap wallet")
(cli::SWAP_WALLET_USER, po::value<string>(), "rpc user name for the swap wallet")
(cli::SWAP_WALLET_PASS, po::value<string>(), "rpc password for the swap wallet")
(cli::SWAP_COIN, po::value<string>(), "swap coin currency (BTC/LTC/QTUM/DASH/DOGE/ETH)")
(cli::SWAP_COIN, po::value<string>(), "swap coin currency (BTC/LTC/QTUM/DASH/DOGE/ETH/ERC20)")
(cli::TOKEN_CONTRACT, po::value<string>(), "ERC-20 token contract address (0x + 40 hex chars), required when swap_coin=erc20")
(cli::SWAP_AMOUNT, po::value<Positive<Amount>>(), "swap amount in the smallest unit of the coin (e.g. satoshi for BTC)")
(cli::SWAP_FEERATE, po::value<Positive<Amount>>(), "specific feerate you are willing to pay (the smallest unit of the coin per KB)")
(cli::SWAP_BEAM_SIDE, "should be always set by the swap party who owns BEAM")
Expand Down
1 change: 1 addition & 0 deletions utility/cli/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ namespace beam
extern const char* ESTIMATE_SWAP_FEERATE;
extern const char* GET_BALANCE;
extern const char* SWAP_COIN;
extern const char* TOKEN_CONTRACT;
extern const char* SWAP_BEAM_SIDE;
extern const char* SWAP_TX_HISTORY;
extern const char* NODE_POLL_PERIOD;
Expand Down
34 changes: 30 additions & 4 deletions wallet/api/cli/api_cli_swap.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,24 @@ class ApiCliSwap
}

private:
// Erc20Token is deliberately excluded from ethereum::IsEthereumBased (its
// contract is per-offer, not one of the fixed Dai/Usdt/WBTC entries with a
// static settings entry), but it still rides the same ethereum RPC
// connection as those coins for connectivity- and gas-price-related
// queries below.
[[nodiscard]] static bool usesEthConnection(AtomicSwapCoin swapCoin)
{
return ethereum::IsEthereumBased(swapCoin) || swapCoin == AtomicSwapCoin::Erc20Token;
}

[[nodiscard]] beam::Amount getCoinAvailable(AtomicSwapCoin swapCoin) const override
{
if (swapCoin == AtomicSwapCoin::Erc20Token)
{
// Erc20Token has no single fixed contract; use getTokenAvailable instead.
return 0;
}

if (ethereum::IsEthereumBased(swapCoin))
{
return _swapEthClient ? _swapEthClient->GetAvailable(swapCoin) : 0;
Expand All @@ -75,9 +91,19 @@ class ApiCliSwap
return swapClient ? swapClient->GetAvailable() : 0;
}

[[nodiscard]] boost::optional<beam::Amount> getTokenAvailable(const std::string& tokenContract, uint8_t decimals) const override
{
if (!_swapEthClient)
{
return boost::none;
}

return _swapEthClient->GetTokenAvailable(tokenContract, decimals);
}

[[nodiscard]] beam::Amount getRecommendedFeeRate(AtomicSwapCoin swapCoin) const override
{
if (ethereum::IsEthereumBased(swapCoin))
if (usesEthConnection(swapCoin))
{
return _swapEthClient ? _swapEthClient->GetRecommendedFeeRate() : 0;
}
Expand All @@ -89,7 +115,7 @@ class ApiCliSwap

[[nodiscard]] beam::Amount getMinFeeRate(AtomicSwapCoin swapCoin) const override
{
if (ethereum::IsEthereumBased(swapCoin))
if (usesEthConnection(swapCoin))
{
return _swapEthClient ? _swapEthClient->GetSettings().GetMinFeeRate() : 0;
}
Expand All @@ -101,7 +127,7 @@ class ApiCliSwap

[[nodiscard]] beam::Amount getMaxFeeRate(AtomicSwapCoin swapCoin) const override
{
if (ethereum::IsEthereumBased(swapCoin))
if (usesEthConnection(swapCoin))
{
return _swapEthClient ? _swapEthClient->GetSettings().GetMaxFeeRate() : 0;
}
Expand All @@ -118,7 +144,7 @@ class ApiCliSwap

[[nodiscard]] bool isCoinClientConnected(AtomicSwapCoin swapCoin) const override
{
if (ethereum::IsEthereumBased(swapCoin))
if (usesEthConnection(swapCoin))
{
return _swapEthClient ? _swapEthClient->IsConnected() : 0;
}
Expand Down
40 changes: 40 additions & 0 deletions wallet/api/cli/swap_eth_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ namespace
{
const unsigned int kBalanceUpdateInterval = 10 * 1000; // 10 seconds
const unsigned int kPriceGasUpdateInterval = 60 * 1000; // 1 minute
// a watched token contract is polled until nobody has asked for its
// balance this long, then its watch and cache entries are dropped
constexpr std::chrono::minutes kTokenWatchIdleTimeout{10};
}

SwapEthClient::SwapEthClient(
Expand Down Expand Up @@ -53,6 +56,26 @@ Amount SwapEthClient::GetAvailable(beam::wallet::AtomicSwapCoin swapCoin) const
return 0;
}

boost::optional<Amount> SwapEthClient::GetTokenAvailable(const std::string& tokenContract, uint8_t decimals)
{
const auto now = std::chrono::steady_clock::now();
auto [watched, isNew] = _watchedTokens.try_emplace(tokenContract, WatchedToken{decimals, now});
watched->second.m_lastUse = now;

auto iter = _tokenBalances.find(tokenContract);
if (iter != _tokenBalances.end())
{
return iter->second;
}

if (isNew && GetSettings().IsActivated())
{
GetAsync()->GetTokenBalance(tokenContract, decimals);
}

return boost::none;
}

Amount SwapEthClient::GetRecommendedFeeRate() const
{
return _recommendedFeeRate;
Expand All @@ -74,6 +97,18 @@ void SwapEthClient::requestBalance()
{
GetAsync()->GetBalance(token);
}
const auto deadline = std::chrono::steady_clock::now() - kTokenWatchIdleTimeout;
for (auto it = _watchedTokens.begin(); it != _watchedTokens.end();)
{
if (it->second.m_lastUse < deadline)
{
_tokenBalances.erase(it->first);
it = _watchedTokens.erase(it);
continue;
}
GetAsync()->GetTokenBalance(it->first, it->second.m_decimals);
++it;
}
}
}

Expand All @@ -96,6 +131,11 @@ void SwapEthClient::OnBalance(beam::wallet::AtomicSwapCoin swapCoin, beam::Amoun
_balances[swapCoin] = balance;
}

void SwapEthClient::OnTokenBalance(const std::string& tokenContract, beam::Amount balance)
{
_tokenBalances[tokenContract] = balance;
}

void SwapEthClient::OnEstimatedGasPrice(Amount feeRate)
{
_recommendedFeeRate = feeRate;
Expand Down
16 changes: 16 additions & 0 deletions wallet/api/cli/swap_eth_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <boost/optional.hpp>
#include <chrono>
#include "wallet/transactions/swaps/bridges/ethereum/client.h"

class SwapEthClient : public beam::ethereum::Client
Expand All @@ -29,12 +31,19 @@ class SwapEthClient : public beam::ethereum::Client
beam::Amount GetRecommendedFeeRate() const;
bool IsConnected() const;

// Balance of an arbitrary ERC-20 contract, in wallet units for the given
// decimals. Returns boost::none until the first refresh cycle has answered
// for this contract; the contract is registered for polling as a side
// effect and dropped again once nobody has asked about it for a while.
boost::optional<beam::Amount> GetTokenAvailable(const std::string& tokenContract, uint8_t decimals);

private:
void requestBalance();
void requestRecommendedFeeRate();

void OnStatus(Status status) override;
void OnBalance(beam::wallet::AtomicSwapCoin swapCoin, beam::Amount balance) override;
void OnTokenBalance(const std::string& tokenContract, beam::Amount balance) override;
void OnEstimatedGasPrice(beam::Amount feeRate) override;
void OnCanModifySettingsChanged(bool canModify) override;
void OnChangedSettings() override;
Expand All @@ -44,6 +53,13 @@ class SwapEthClient : public beam::ethereum::Client
beam::io::Timer::Ptr _timer;
beam::io::Timer::Ptr _feeTimer;
std::map<beam::wallet::AtomicSwapCoin, beam::Amount> _balances;
struct WatchedToken
{
uint8_t m_decimals;
std::chrono::steady_clock::time_point m_lastUse;
};
std::map<std::string, WatchedToken> _watchedTokens;
std::map<std::string, beam::Amount> _tokenBalances;
beam::Amount _recommendedFeeRate = 0;
Status _status;
};
12 changes: 12 additions & 0 deletions wallet/api/i_swaps_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <boost/optional.hpp>
#include "wallet/core/common.h"
#include "wallet/transactions/swaps/common.h"

Expand All @@ -25,10 +26,21 @@ namespace beam::wallet
typedef std::shared_ptr<ISwapsProvider> Ptr;

[[nodiscard]] virtual Amount getCoinAvailable(AtomicSwapCoin swapCoin) const = 0;
// Balance for an arbitrary per-offer ERC-20 contract, in wallet units for
// the given decimals. boost::none means the contract hasn't been observed
// long enough for a live query to have answered yet.
[[nodiscard]] virtual boost::optional<Amount> getTokenAvailable(const std::string& tokenContract, uint8_t decimals) const = 0;
[[nodiscard]] virtual Amount getRecommendedFeeRate(AtomicSwapCoin swapCoin) const = 0;
[[nodiscard]] virtual Amount getMinFeeRate(AtomicSwapCoin swapCoin) const = 0;
[[nodiscard]] virtual Amount getMaxFeeRate(AtomicSwapCoin swapCoin) const = 0;
[[nodiscard]] virtual const SwapOffersBoard& getSwapOffersBoard() const = 0;
[[nodiscard]] virtual bool isCoinClientConnected(AtomicSwapCoin swapCoin) const = 0;
};

// True when a balance check should pass: either the balance isn't known yet
// (first time this contract is seen) or the known balance covers the total.
inline bool IsSwapAmountAvailable(const boost::optional<Amount>& available, Amount total)
{
return !available || *available > total;
}
}
15 changes: 15 additions & 0 deletions wallet/api/v6_0/v6_api_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,21 @@ namespace beam::wallet
Amount swapFeeRate = 0;
Height offerLifetime = 15;
std::string comment;
// Only meaningful when swapCoin == AtomicSwapCoin::Erc20Token: the
// per-offer ERC-20 contract address ("0x" + 40 hex chars) and the
// symbol()/decimals() the caller asserts for it. Unlike the CLI (which
// queries the contract live and asks for interactive confirmation),
// the API is not a live-query trust boundary, so the caller supplies
// these directly; they are re-validated (format + decimals bound)
// when the offer is created/accepted, and the wallet re-derives the
// authoritative values from the chain when the swap transaction runs.
std::string tokenContract;
std::string tokenSymbol;
uint8_t tokenDecimals = 0;
// Non-zero when the BEAM leg of the swap carries a Confidential Asset
// instead of plain BEAM; validated (known asset, sufficient balance)
// when the offer is created.
Asset::ID beamAssetId = Asset::s_InvalidID;
};

struct OffersList
Expand Down
Loading
Loading