Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
724c735
Add yielder crate with Yo earn provider
gemdev111 Mar 11, 2026
72f68aa
Integrate yielder into gateway for earn operations
gemdev111 Mar 11, 2026
f5aeaee
Refactor Yo client and provider
gemdev111 Mar 12, 2026
48a0144
Add unit tests for conversion and slippage
gemdev111 Mar 12, 2026
8b643f5
Rename yielder/yo methods for clarity
gemdev111 Mar 12, 2026
dd4da0a
Update YoAsset to checksum
gemdev111 Mar 12, 2026
d2bc619
Extracted mapping logic to mapper
gemdev111 Mar 16, 2026
1d3a7d9
Removed PROVIDER const, improved visability for GAS_LIMIT
gemdev111 Mar 16, 2026
f775106
update naming consistence
gemdev111 Mar 16, 2026
1cd5ef0
Simplify errors
gemdev111 Mar 16, 2026
bfd40ac
Yo client: add ERC-4626 interface, typed eth_call, and multicall3 bat…
gemdev111 Mar 17, 2026
de4c4e7
Refactor: move slippage to gem_evm, rename yielder methods for consis…
gemdev111 Mar 17, 2026
017f142
Refactor yielder crate architecture
gemdev111 Mar 17, 2026
d6b7f1c
Extract shared alien types and client factories to gem_jsonrpc and ge…
gemdev111 Mar 17, 2026
1b77b40
Migrate swapper to shared alien types from gem_jsonrpc
gemdev111 Mar 17, 2026
9560678
Refactor yielder crate architecture
gemdev111 Mar 17, 2026
128f77d
Added name to earn provider
gemdev111 Mar 17, 2026
dfc3197
Pass token IDs to earn balance fetch to skip unnecessary RPC calls
gemdev111 Mar 17, 2026
5341ab4
Refactor yielder crate for clarity and testability
gemdev111 Mar 17, 2026
e697c6d
Refactor EVM client factory: remove Option, add yielder client_factory
gemdev111 Mar 17, 2026
37d30ef
Improve Yielder and AlienProviderWrapper design
gemdev111 Mar 18, 2026
4c5eec2
Removed dead code
gemdev111 Mar 18, 2026
a3662e3
Improve naming
gemdev111 Mar 18, 2026
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
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ members = [
"crates/tracing",
"crates/claude",
"crates/support",
"crates/yielder",
]

[workspace.dependencies]
Expand Down
9 changes: 9 additions & 0 deletions crates/gem_evm/src/contracts/erc4626.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use alloy_sol_types::sol;

sol! {
interface IERC4626 {
function balanceOf(address account) external view returns (uint256);
function totalAssets() external view returns (uint256);
function totalSupply() external view returns (uint256);
}
}
4 changes: 3 additions & 1 deletion crates/gem_evm/src/contracts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod erc1155;
pub mod erc20;
pub mod erc721;
pub mod erc1155;
pub mod erc4626;

pub use erc20::IERC20;
pub use erc721::IERC721;
pub use erc1155::IERC1155;
pub use erc4626::IERC4626;
1 change: 1 addition & 0 deletions crates/gem_evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod jsonrpc;
pub mod message;
pub mod monad;
pub mod multicall3;
pub mod slippage;
pub mod permit2;
#[cfg(feature = "rpc")]
pub mod registry;
Expand Down
21 changes: 21 additions & 0 deletions crates/gem_evm/src/rpc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,25 @@ impl<C: Client + Clone> EthereumClient<C> {

Ok(multicall_results)
}

#[cfg(feature = "rpc")]
pub async fn call_contract<T: SolCall>(&self, target: Address, sol_call: T) -> Result<T::Return, Box<dyn std::error::Error + Sync + Send>> {
let call_data = hex::encode_prefixed(sol_call.abi_encode());
let params = json!([{ "to": target.to_string(), "data": call_data }, Self::latest_block_parameter()]);
let result: String = self.client.call("eth_call", params).await?;
let result_data = hex::decode(&result)?;
Ok(T::abi_decode_returns(&result_data)?)
}

#[cfg(feature = "rpc")]
pub async fn multicall3_map<T, R, const N: usize>(
&self,
items: &[T],
build: impl Fn(&T) -> [Call3; N],
decode: impl Fn(&[MulticallResult]) -> Result<R, Box<dyn std::error::Error + Send + Sync>>,
) -> Result<Vec<R>, Box<dyn std::error::Error + Sync + Send>> {
let calls = items.iter().flat_map(&build).collect();
let results = self.multicall3(calls).await?;
results.chunks(N).map(&decode).collect()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ mod tests {
use super::*;

#[test]
fn test_apply_slippage() {
fn test_apply_slippage_in_bp() {
assert_eq!(apply_slippage_in_bp(&U256::from(100), 300), U256::from(97));
assert_eq!(apply_slippage_in_bp(&100_u128, 300), 97_u128);
assert_eq!(apply_slippage_in_bp(&1000_u64, 500), 950_u64);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use std::sync::Arc;

use gem_client::ClientError;
use gem_jsonrpc::RpcClientError;
use primitives::Chain;

use crate::client::JsonRpcClient;
use crate::rpc::{RpcClient as GenericRpcClient, RpcClientError, RpcProvider as GenericRpcProvider};

#[derive(Debug, Clone)]
pub enum AlienError {
Expand Down Expand Up @@ -28,9 +33,9 @@ impl AlienError {
impl std::fmt::Display for AlienError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::RequestError { msg } => write!(f, "Request error: {}", msg),
Self::ResponseError { msg } => write!(f, "Response error: {}", msg),
Self::Http { status, .. } => write!(f, "HTTP error: status {}", status),
Self::RequestError { msg } => write!(f, "Request error: {msg}"),
Self::ResponseError { msg } => write!(f, "Response error: {msg}"),
Self::Http { status, .. } => write!(f, "HTTP error: status {status}"),
}
}
}
Expand All @@ -40,9 +45,20 @@ impl std::error::Error for AlienError {}
impl RpcClientError for AlienError {
fn into_client_error(self) -> ClientError {
match self {
Self::RequestError { msg } => ClientError::Network(msg),
Self::ResponseError { msg } => ClientError::Network(msg),
Self::RequestError { msg } | Self::ResponseError { msg } => ClientError::Network(msg),
Self::Http { status, .. } => ClientError::Http { status, body: Vec::new() },
}
}
}

pub type RpcClient = GenericRpcClient<AlienError>;

pub trait RpcProvider: GenericRpcProvider<Error = AlienError> {}

impl<T> RpcProvider for T where T: GenericRpcProvider<Error = AlienError> {}

pub fn create_client(provider: Arc<dyn RpcProvider>, chain: Chain) -> Result<JsonRpcClient<RpcClient>, AlienError> {
let endpoint = provider.get_endpoint(chain)?;
let client = RpcClient::new(endpoint, provider);
Ok(JsonRpcClient::new(client))
}
2 changes: 2 additions & 0 deletions crates/gem_jsonrpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod types;

#[cfg(feature = "client")]
pub mod alien;
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "client")]
Expand Down
13 changes: 13 additions & 0 deletions crates/primitives/src/earn_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,16 @@ pub enum EarnType {
Deposit(DelegationValidator),
Withdraw(Delegation),
}

impl EarnType {
pub fn provider(&self) -> &DelegationValidator {
match self {
EarnType::Deposit(provider) => provider,
EarnType::Withdraw(delegation) => &delegation.validator,
}
}

pub fn provider_id(&self) -> &str {
&self.provider().id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&self.provider().id
&self.provider()

}
}
8 changes: 8 additions & 0 deletions crates/primitives/src/yield_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ use typeshare::typeshare;
pub enum YieldProvider {
Yo,
}

impl YieldProvider {
pub fn name(&self) -> &str {
match self {
Self::Yo => "Yo",
}
}
}
3 changes: 2 additions & 1 deletion crates/swapper/src/alien/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::{
};

use super::{AlienError, Target};
use gem_jsonrpc::{RpcProvider as GenericRpcProvider, RpcResponse};
use gem_jsonrpc::RpcResponse;
use gem_jsonrpc::rpc::RpcProvider as GenericRpcProvider;
use primitives::Chain;

#[allow(unused)]
Expand Down
11 changes: 2 additions & 9 deletions crates/swapper/src/alien/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
pub mod error;
pub mod mock;
#[cfg(feature = "reqwest_provider")]
pub mod reqwest_provider;

pub use error::AlienError;
pub use gem_jsonrpc::{HttpMethod, RpcClient as GenericRpcClient, RpcProvider as GenericRpcProvider, Target};

pub type RpcClient = GenericRpcClient<AlienError>;

pub trait RpcProvider: GenericRpcProvider<Error = AlienError> {}

impl<T> RpcProvider for T where T: GenericRpcProvider<Error = AlienError> {}
pub use gem_jsonrpc::alien::{AlienError, RpcClient, RpcProvider};
pub use gem_jsonrpc::{HttpMethod, Target};
3 changes: 2 additions & 1 deletion crates/swapper/src/alien/reqwest_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use primitives::{Chain, node_config::get_nodes_for_chain};

use async_trait::async_trait;
use futures::TryFutureExt;
use gem_jsonrpc::{RpcProvider as GenericRpcProvider, RpcResponse};
use gem_jsonrpc::RpcResponse;
use gem_jsonrpc::rpc::RpcProvider as GenericRpcProvider;
use reqwest::Client;

#[derive(Debug)]
Expand Down
6 changes: 4 additions & 2 deletions crates/swapper/src/chainflip/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ use super::{
seed::generate_random_seed,
tx_builder,
};
use gem_evm::slippage::apply_slippage_in_bp;

use crate::{
FetchQuoteData, ProviderData, ProviderType, Quote, QuoteRequest, Route, SwapResult, Swapper, SwapperChainAsset, SwapperError, SwapperProvider, SwapperQuoteData,
alien::RpcProvider, amount_to_value, approval::check_approval_erc20, config::DEFAULT_CHAINFLIP_FEE_BPS, cross_chain::VaultAddresses, slippage,
alien::RpcProvider, amount_to_value, approval::check_approval_erc20, config::DEFAULT_CHAINFLIP_FEE_BPS, cross_chain::VaultAddresses,
};
use primitives::{ChainType, chain::Chain, swap::QuoteAsset};

Expand Down Expand Up @@ -218,7 +220,7 @@ where
})
} else if from_asset.chain.chain_type() == ChainType::Bitcoin {
let output_amount: U256 = quote.to_value.parse()?;
let min_output_amount = slippage::apply_slippage_in_bp(&output_amount, quote.data.slippage_bps);
let min_output_amount = apply_slippage_in_bp(&output_amount, quote.data.slippage_bps);
VaultSwapExtras::Bitcoin(VaultSwapBtcExtras {
chain,
min_output_amount: BigUint::from_bytes_le(&min_output_amount.to_le_bytes::<32>()),
Expand Down
12 changes: 4 additions & 8 deletions crates/swapper/src/client_factory.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
use gem_evm::rpc::EthereumClient;
use gem_jsonrpc::alien::{self, RpcClient, RpcProvider};
use gem_jsonrpc::client::JsonRpcClient;
use primitives::{Chain, EVMChain};
use std::sync::Arc;

use crate::{
SwapperError,
alien::{RpcClient, RpcProvider},
};
use crate::SwapperError;

pub fn create_client_with_chain(provider: Arc<dyn RpcProvider>, chain: Chain) -> JsonRpcClient<RpcClient> {
let endpoint = provider.get_endpoint(chain).expect("Failed to get endpoint for chain");
let client = RpcClient::new(endpoint, provider);
JsonRpcClient::new(client)
alien::create_client(provider, chain).expect("failed to create client for chain")
}

pub fn create_eth_client(provider: Arc<dyn RpcProvider>, chain: Chain) -> Result<EthereumClient<RpcClient>, SwapperError> {
let evm_chain = EVMChain::from_chain(chain).ok_or(SwapperError::NotSupportedChain)?;
let client = create_client_with_chain(provider, chain);
let client = alien::create_client(provider, chain).map_err(|_| SwapperError::NotSupportedChain)?;
Ok(EthereumClient::new(client, evm_chain))
}

Expand Down
1 change: 0 additions & 1 deletion crates/swapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub mod permit2_data;
pub mod proxy;
pub mod referrer;
pub mod relay;
pub mod slippage;
pub mod swapper;
pub mod thorchain;
pub mod uniswap;
Expand Down
3 changes: 2 additions & 1 deletion crates/swapper/src/uniswap/v3/commands.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{SwapperError, SwapperMode, eth_address, models::*, slippage::apply_slippage_in_bp};
use crate::{SwapperError, SwapperMode, eth_address, models::*};
use gem_evm::slippage::apply_slippage_in_bp;
use gem_evm::uniswap::command::{ADDRESS_THIS, PayPortion, Permit2Permit, Sweep, Transfer, UniversalRouterCommand, UnwrapWeth, V3SwapExactIn, WrapEth};

use alloy_primitives::{Address, Bytes, U256};
Expand Down
3 changes: 2 additions & 1 deletion crates/swapper/src/uniswap/v3/provider.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use gem_evm::slippage::apply_slippage_in_bp;

use crate::{
FetchQuoteData, Permit2ApprovalData, ProviderData, ProviderType, Quote, QuoteRequest, Swapper, SwapperError, SwapperQuoteData,
alien::{RpcClient, RpcProvider},
approval::{check_approval_erc20_with_client, check_approval_permit2_with_client},
eth_address,
models::*,
slippage::apply_slippage_in_bp,
uniswap::{
deadline::get_sig_deadline,
fee_token::get_fee_token,
Expand Down
3 changes: 2 additions & 1 deletion crates/swapper/src/uniswap/v4/commands.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::str::FromStr;

use crate::{QuoteRequest, Route, SwapperError, SwapperMode, eth_address, slippage::apply_slippage_in_bp};
use crate::{QuoteRequest, Route, SwapperError, SwapperMode, eth_address};
use gem_evm::slippage::apply_slippage_in_bp;
use alloy_primitives::{Address, U256};
use gem_evm::uniswap::{
actions::V4Action::{SETTLE, SWAP_EXACT_IN, TAKE},
Expand Down
3 changes: 2 additions & 1 deletion crates/swapper/src/uniswap/v4/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use alloy_primitives::{Address, U256, hex::encode_prefixed as HexEncode};
use async_trait::async_trait;
use std::{collections::HashSet, fmt, str::FromStr, sync::Arc, vec};

use gem_evm::slippage::apply_slippage_in_bp;

use crate::{
FetchQuoteData, Permit2ApprovalData, ProviderData, ProviderType, Quote, QuoteRequest, Swapper, SwapperChainAsset, SwapperError, SwapperProvider, SwapperQuoteData,
alien::{RpcClient, RpcProvider},
approval::evm::{check_approval_erc20_with_client, check_approval_permit2_with_client},
eth_address,
slippage::apply_slippage_in_bp,
uniswap::{
deadline::get_sig_deadline,
fee_token::get_fee_token,
Expand Down
30 changes: 30 additions & 0 deletions crates/yielder/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "yielder"
version.workspace = true
edition.workspace = true
license.workspace = true

[features]
default = []
yield_integration_tests = [
"gem_jsonrpc/reqwest",
"gem_client/reqwest",
"tokio/rt-multi-thread",
]

[dependencies]
alloy-primitives = { workspace = true }
alloy-sol-types = { workspace = true }
num-bigint = { workspace = true }
gem_client = { path = "../gem_client" }
gem_evm = { path = "../gem_evm", features = ["rpc"] }
gem_jsonrpc = { path = "../gem_jsonrpc" }
primitives = { path = "../primitives" }
async-trait = { workspace = true }
futures = { workspace = true }

[dev-dependencies]
gem_client = { path = "../gem_client", features = ["reqwest"] }
gem_jsonrpc = { path = "../gem_jsonrpc", features = ["reqwest"] }
reqwest = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
17 changes: 17 additions & 0 deletions crates/yielder/src/client_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use gem_evm::rpc::EthereumClient;
use gem_jsonrpc::alien::{self, RpcClient, RpcProvider};
use gem_jsonrpc::client::JsonRpcClient;
use primitives::{Chain, EVMChain};
use std::sync::Arc;

use crate::YielderError;

pub fn create_client(provider: Arc<dyn RpcProvider>, chain: Chain) -> Result<JsonRpcClient<RpcClient>, YielderError> {
alien::create_client(provider, chain).map_err(|_| YielderError::NotSupportedChain)
}

pub fn create_eth_client(provider: Arc<dyn RpcProvider>, chain: Chain) -> Result<EthereumClient<RpcClient>, YielderError> {
let evm_chain = EVMChain::from_chain(chain).ok_or(YielderError::NotSupportedChain)?;
let client = create_client(provider, chain)?;
Ok(EthereumClient::new(client, evm_chain))
}
Loading
Loading