Skip to content

Commit 2c87c92

Browse files
authored
Merge pull request #5 from DefichainCommunity/main
add native token support to v2 and v3
2 parents eed0601 + e896cc8 commit 2c87c92

13 files changed

Lines changed: 294 additions & 213 deletions

File tree

frontend/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ log = "0.4"
1616
console_log = "1.0"
1717
alloy = {version = "1.1", default-features = false }
1818
dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false }
19+
enum-table = "2.1"
1920

2021
[profile]
2122

frontend/src/app.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ pub fn App() -> Element {
139139

140140
// ---- FIXED TOP BAR ----
141141
div {
142-
class: "fixed top-0 left-0 w-full flex items-center justify-between
143-
px-8 py-4 backdrop-blur-sm bg-black/20 z-50",
144-
142+
// class: "fixed top-0 left-0 w-full flex items-center justify-between
143+
// px-8 py-4 backdrop-blur-sm bg-black/20 z-50",
144+
class: "w-full flex flex-wrap items-center justify-between gap-2 p-4",
145145
div { class: "relative",
146146
h1 {
147147
class: "text-xl font-bold tracking-wide text-defichain",
@@ -159,7 +159,7 @@ pub fn App() -> Element {
159159
}
160160

161161
// Tabs
162-
div { class: "flex space-x-4 mt-6",
162+
div { class: "flex flex-wrap flex-grow justify-center gap-2 mt-6",
163163
button {
164164
class: button_class(Tab::Wrap, active_tab()),
165165
onclick: move |_| active_tab.set(Tab::Wrap),

frontend/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod app;
2+
mod config;
23
mod components;
34
mod metamask;
45
mod vanillaswap;

frontend/src/metamask.js

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ const wrapperRouterAbi = [
2727
// Uniswap V2
2828
const uniswapV2RouterAbi = [
2929
"function factory() view returns (address)",
30-
"function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) returns (uint256[])"
30+
"function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) returns (uint256[])",
31+
"function swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline)",
32+
"function swapExactETHForTokensSupportingFeeOnTransferTokens(uint256 amountOutMin, address[] calldata path, address to, uint256 deadline) payable"
3133
];
3234
const uniswapV2FactoryAbi = [
3335
"function allPairsLength() view returns (uint256)",
@@ -43,10 +45,12 @@ const uniswapV2PairAbi = [
4345
const uniswapV3PairAbi = [ "function slot0() view returns (uint160 sqrtPriceX96, int24, uint16, uint16, uint16, uint8, bool)", "function liquidity() view returns (uint128)" ];
4446

4547
const uniswapV3RouterAbi = [
46-
// exactInputSingle params in struct form
47-
//"function exactInputSingle((address,address,uint24,address,uint256,uint256,uint160)) external payable returns (uint256 amountOut)",
4848
"function exactInput((bytes path,address recipient,uint256 amountIn,uint256 amountOutMinimum)) external payable returns (uint256 amountOut)"
4949
];
50+
const wethAbi = [
51+
"function withdraw(uint256 wad) external",
52+
"function balanceOf(address owner) view returns (uint256)"
53+
];
5054

5155
/**
5256
* helper JSON stringify that converts BigInt to string
@@ -102,14 +106,20 @@ export function js_on_accounts_changed(callback) {
102106
}
103107

104108
// ERC20
105-
export async function js_get_token_balance(user, token) {
109+
export async function js_get_token_balance(user, token, isNative) {
106110
try {
107111
if (!window.ethereum) throw new Error("MetaMask not installed");
108112
await window.ethereum.request({ method: 'eth_requestAccounts' });
109113
let provider = new ethers.BrowserProvider(window.ethereum);
110114

111-
const erc20 = new ethers.Contract(token, erc20Abi, provider);
112-
const [bal, decimals] = await Promise.all([erc20.balanceOf(user), 18]);
115+
let bal, decimals;
116+
117+
if (isNative) {
118+
[bal, decimals] = await Promise.all([provider.getBalance(user), 18]);
119+
}else{
120+
const erc20 = new ethers.Contract(token, erc20Abi, provider);
121+
[bal, decimals] = await Promise.all([erc20.balanceOf(user), erc20.decimals()]);
122+
}
113123
return {
114124
ok: true,
115125
value: JSON.stringify(ethers.formatUnits(bal, decimals))
@@ -337,37 +347,61 @@ export async function js_get_uniswap_v2_pairs(routerAddr) {
337347
}
338348
}
339349

340-
export async function js_uniswap_v2_swap_tokens(tokenIn, tokenOut, amountIn, amountOutMin, routerAddress) {
350+
export async function js_uniswap_v2_swap_tokens(tokenIn, tokenOut, amountIn, amountOutMin, routerAddress, isNativeIn, isNativeOut) {
341351
try {
342352
if (!window.ethereum) throw new Error("MetaMask not installed");
343353
await window.ethereum.request({ method: 'eth_requestAccounts' });
344354
let provider = new ethers.BrowserProvider(window.ethereum);
345355
let signer = await provider.getSigner();
346356

347-
348357
const router = new ethers.Contract(routerAddress, uniswapV2RouterAbi, signer);
349358

350359
const tokenInContract = new ethers.Contract(tokenIn, erc20Abi, signer);
351360
const decimals = await tokenInContract.decimals();
352-
const amount_in_u256 = amountIn;//thers.parseUnits(amountIn,decimals);
353-
354-
// Approve
355-
const allowance = await tokenInContract.allowance(signer, routerAddress);
356-
if (allowance < amount_in_u256){
357-
const approve_tx = await tokenInContract.approve(routerAddress, amount_in_u256);
358-
await approve_tx.wait();
359-
}
361+
const amount_in_u256 = amountIn;
360362

361363
const path = [tokenIn, tokenOut];
362364
const deadline = Math.floor(Date.now() / 1000) + 60 * 10;
363365

364-
const tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
365-
amountIn,
366-
amountOutMin,
367-
path,
368-
await signer.getAddress(),
369-
deadline,
370-
);
366+
let tx;
367+
if (isNativeIn){
368+
tx = await router.swapExactETHForTokensSupportingFeeOnTransferTokens(
369+
amountOutMin,
370+
path,
371+
await signer.getAddress(),
372+
deadline,
373+
{
374+
value: amountIn
375+
}
376+
);
377+
}else{
378+
// Approve
379+
const allowance = await tokenInContract.allowance(signer, routerAddress);
380+
if (allowance < amount_in_u256){
381+
const approve_tx = await tokenInContract.approve(routerAddress, amount_in_u256);
382+
await approve_tx.wait();
383+
}
384+
385+
386+
if (isNativeOut){
387+
tx = await router.swapExactTokensForETHSupportingFeeOnTransferTokens(
388+
amountIn,
389+
amountOutMin,
390+
path,
391+
await signer.getAddress(),
392+
deadline,
393+
);
394+
}else{
395+
tx = await router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
396+
amountIn,
397+
amountOutMin,
398+
path,
399+
await signer.getAddress(),
400+
deadline,
401+
);
402+
}
403+
404+
}
371405
const receipt = await tx.wait();
372406

373407
return {
@@ -428,7 +462,7 @@ function encodePath(tokenIn, fee, tokenOut) {
428462
]);
429463
}
430464

431-
export async function js_uniswap_v3_swap_tokens(tokenIn, tokenOut, amountIn, amountOutMin, fee, routerAddress) {
465+
export async function js_uniswap_v3_swap_tokens(tokenIn, tokenOut, amountIn, amountOutMin, fee, routerAddress, isNativeIn, isNativeOut) {
432466
try {
433467
if (!window.ethereum) throw new Error("MetaMask not installed");
434468
await window.ethereum.request({ method: 'eth_requestAccounts' });
@@ -450,17 +484,23 @@ export async function js_uniswap_v3_swap_tokens(tokenIn, tokenOut, amountIn, amo
450484
}
451485

452486
const path = encodePath(tokenIn,fee,tokenOut);
453-
console.log("Path len ",path.length);
454487
const recipient = await signer.getAddress();
455488
const params = {
456489
path:path,
457490
recipient:recipient,
458491
amountIn:amount_in_u256,
459492
amountOutMinimum: amountOutMin
460493
};
461-
const tx = await router.exactInput(params,{value: 0n});
494+
const tx = await router.exactInput(params,isNativeIn ? { value: amountIn } : {});
462495
const receipt = await tx.wait();
463-
496+
if (isNativeOut) {
497+
const wethContract = new ethers.Contract(tokenOut, wethAbi, signer);
498+
const balance = await wethContract.balanceOf(recipient);
499+
if (balance > 0n) {
500+
const unwrapTx = await wethContract.withdraw(balance);
501+
await unwrapTx.wait();
502+
}
503+
}
464504
return {
465505
ok: true,
466506
value: JSON.stringify(`${receipt.hash}`)

frontend/src/metamask.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extern "C" {
1515
async fn js_connect_metamask() -> JsValue;
1616
pub fn js_on_chain_changed(callback: &Closure<dyn FnMut(u32)>);
1717
pub fn js_on_accounts_changed(callback: &Closure<dyn FnMut(Vec<JsValue>)>);
18-
async fn js_get_token_balance(user: &str, token: &str) -> JsValue;
18+
async fn js_get_token_balance(user: &str, token: &str, is_native: bool) -> JsValue;
1919
}
2020

2121
pub fn js_parse<T: DeserializeOwned>(js: JsValue) -> Result<T, String> {
@@ -53,6 +53,6 @@ pub async fn connect_metamask() -> Result<MetamaskInfo, Box<dyn Error>>{
5353
js_try!(js_connect_metamask() => MetamaskInfo)
5454
}
5555

56-
pub async fn get_token_balance(user: &str, token: &str) -> Result<String,Box<dyn Error>>{
57-
js_try!(js_get_token_balance(user, token) => String)
56+
pub async fn get_token_balance(user: &str, token: &str, is_native: bool) -> Result<String,Box<dyn Error>>{
57+
js_try!(js_get_token_balance(user, token, is_native) => String)
5858
}

frontend/src/metamask/uniswap_v2.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern "C" {
1313
// uniswap v2
1414
async fn js_get_uniswap_v2_pairs(router_address: &str) -> JsValue;
1515
async fn js_uniswap_v2_swap_tokens(token_in: &str, token_out: &str, amount_in: &str,
16-
amount_out_min: &str, router_address: &str) -> JsValue;
16+
amount_out_min: &str, router_address: &str, is_native_in: bool, is_native_out: bool) -> JsValue;
1717
}
1818

1919

@@ -43,6 +43,8 @@ pub async fn uniswap_v2_swap_tokens(
4343
amount_in: &str,
4444
amount_out_min: &str,
4545
router_address: &str,
46+
is_native_in: bool,
47+
is_native_out: bool,
4648
) -> Result<String, String> {
47-
js_try!(js_uniswap_v2_swap_tokens(token_in,token_out,amount_in,amount_out_min,router_address) => String)
49+
js_try!(js_uniswap_v2_swap_tokens(token_in,token_out,amount_in,amount_out_min,router_address, is_native_in, is_native_out) => String)
4850
}

frontend/src/metamask/uniswap_v3.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern "C" {
1313
//uniswap v3
1414
async fn js_get_uniswap_v3_pool_states(pools: Vec<JsValue>) -> JsValue;
1515
async fn js_uniswap_v3_swap_tokens(token_in: &str, token_out: &str, amount_in: &str,
16-
amount_out_min: &str, fee: &str, router_address: &str) -> JsValue;
16+
amount_out_min: &str, fee: &str, router_address: &str, is_native_in: bool, is_native_out: bool) -> JsValue;
1717
}
1818

1919

@@ -40,6 +40,8 @@ pub async fn uniswap_v3_swap_tokens(
4040
amount_out_min: &str,
4141
fee: &str,
4242
router_address: &str,
43+
is_native_in: bool,
44+
is_native_out: bool,
4345
) -> Result<String, String> {
44-
js_try!(js_uniswap_v3_swap_tokens(token_in,token_out,amount_in,amount_out_min,fee,router_address) => String)
46+
js_try!(js_uniswap_v3_swap_tokens(token_in,token_out,amount_in,amount_out_min,fee,router_address, is_native_in, is_native_out) => String)
4547
}

frontend/src/vanillaswap/pools_v2.rs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,12 @@ use alloy::primitives::{utils::parse_units,U256};
44
use crate::components::switch::{Switch, SwitchThumb};
55
use crate::metamask::uniswap_v2::{V2PairInfo, get_uniswap_v2_pairs};
66
use crate::wallet_context::use_wallet;
7-
use super::v2::use_v2_pools;
8-
9-
fn is_zero_or_empty(v: &Option<String>) -> bool {
10-
match v.as_deref() {
11-
None => true,
12-
Some("") => true,
13-
Some("0") => true,
14-
Some("0.0") => true,
15-
Some(s) => {
16-
// also handle cases like "0.0000"
17-
s.trim().parse::<f64>().map(|n| n == 0.0).unwrap_or(false)
18-
}
19-
}
20-
}
7+
use super::v2::{use_v2_pools, is_zero_or_empty};
218

229
#[component]
2310
pub fn PoolV2Pairs() -> Element {
24-
// let mut is_loading = use_signal(|| false);
25-
// let mut pairs = use_signal(|| Vec::<PairInfo>::new());
26-
// let mut error = use_signal(|| None::<String>);
2711
let mut show_zero_liq = use_signal(|| false);
28-
// let mut router_address = use_signal(|| "".to_string());
2912
let pools = use_v2_pools();
30-
// use_effect(move || {
31-
// let wallet = use_wallet();
32-
// let _info = (wallet.info)().clone();
33-
// spawn_local(async move {
34-
// let info =( wallet.info)().clone();
35-
// log::debug!("Chain ID:{}", info.chain_id);
36-
// pairs.set(vec![]);
37-
// if info.chain_id == 1130{ // MainNet
38-
// router_address.set("0x3E8C92491fc73390166BA00725B8F5BD734B8fba".to_string());
39-
// }else if info.chain_id == 1131{ // TestNet
40-
// router_address.set("0x79208eADd9FbC29116108433a38Af62D0fD83850".to_string());
41-
// }else{
42-
// router_address.set("".to_string());
43-
// }
44-
45-
// if !info.address.is_empty() && !router_address.is_empty() {
46-
// is_loading.set(true);
47-
// log::debug!("Router address {}", router_address);
48-
// match get_uniswap_v2_pairs(&router_address()).await {
49-
// Ok(list) => pairs.set(list),
50-
// Err(e) => error.set(Some(e.to_string())),
51-
// }
52-
// is_loading.set(false);
53-
54-
// }
55-
// })
56-
// });
5713

5814
rsx! {
5915
div { class: "p-8 mt-12 glass w-full max-w-4xl flex flex-col gap-6 items-stretch flex-col-sm",
@@ -78,12 +34,6 @@ pub fn PoolV2Pairs() -> Element {
7834
div { class: "flex flex-col gap-3",
7935
for pair in pools.pairs.read().iter().filter(|p| {
8036
show_zero_liq() || (!is_zero_or_empty(&p.reserve0) && !is_zero_or_empty(&p.reserve1))
81-
// if show_zero_liq() {
82-
// true
83-
// } else {
84-
// !is_zero_or_empty(&p.reserve0) &&
85-
// !is_zero_or_empty(&p.reserve1)
86-
// }
8737
}) {
8838
div { class: "p-4 bg-gray-900/60 border border-gray-800 rounded-xl shadow-md
8939
flex flex-col gap-2 hover:bg-gray-900 transition-colors duration-200",

0 commit comments

Comments
 (0)