Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
614b3b1
implement new dex aggregator
GrandSchtroumpf Jan 28, 2026
e7aee82
Implement dex aggregator
GrandSchtroumpf Jan 29, 2026
ab20da6
fix build
GrandSchtroumpf Jan 29, 2026
322b244
fix tradeByTarget
GrandSchtroumpf Jan 30, 2026
4667d67
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Jan 30, 2026
b1c04bb
remove reference to openocean
GrandSchtroumpf Jan 30, 2026
5e7234c
update api
GrandSchtroumpf Jan 30, 2026
da8322d
use GET for cloudflare api
GrandSchtroumpf Jan 30, 2026
7211227
add logs
GrandSchtroumpf Jan 30, 2026
ce6f239
use fromEntries
GrandSchtroumpf Jan 30, 2026
56ee70a
force slippage to be a number
GrandSchtroumpf Jan 30, 2026
74884d0
force types
GrandSchtroumpf Jan 30, 2026
b6e14de
remove logs
GrandSchtroumpf Jan 30, 2026
e89368d
force approval for EIP7702
GrandSchtroumpf Feb 2, 2026
56f753b
fix e2e
GrandSchtroumpf Feb 2, 2026
8936aad
use SDK when useDexAggregator is false
GrandSchtroumpf Feb 3, 2026
8dafe0c
fix submit
GrandSchtroumpf Feb 3, 2026
7bacd02
throw on approval close
GrandSchtroumpf Feb 3, 2026
ae202ee
simplify getApproval
GrandSchtroumpf Feb 3, 2026
23ef1fc
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Feb 3, 2026
f5ee615
extract dex aggregator url in env
GrandSchtroumpf Feb 11, 2026
b58224f
update proxy
GrandSchtroumpf Feb 11, 2026
52a7e02
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Feb 11, 2026
5d5a3a8
update function
GrandSchtroumpf Feb 11, 2026
0bae1b3
update dex aggregator function
GrandSchtroumpf Feb 12, 2026
048c762
use requestPost
GrandSchtroumpf Feb 12, 2026
9f9e2db
Centralized approval
GrandSchtroumpf Feb 13, 2026
0e9689b
put back pre-approve indicator
GrandSchtroumpf Feb 13, 2026
27565b2
update privacy page
GrandSchtroumpf Feb 24, 2026
309c018
disconnect if privacy changed
GrandSchtroumpf Feb 24, 2026
d55c108
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 9, 2026
1217578
improve UI
GrandSchtroumpf Mar 9, 2026
9165cd6
rename Carbon to Carbon DeFi
GrandSchtroumpf Mar 10, 2026
ffec7ab
refactor swap page
GrandSchtroumpf Mar 10, 2026
8f9579c
keep pre-approved if we need to approve a token
GrandSchtroumpf Mar 11, 2026
7802b51
fix E2E
GrandSchtroumpf Mar 11, 2026
739cdc3
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 12, 2026
871627d
improve E2E
GrandSchtroumpf Mar 12, 2026
598c416
improve approval
GrandSchtroumpf Mar 16, 2026
cb7e72e
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 16, 2026
4516b04
fix approval check on swap
GrandSchtroumpf Mar 16, 2026
26f4828
update errors
GrandSchtroumpf Mar 17, 2026
9c3fad1
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 17, 2026
10d527e
fix build
GrandSchtroumpf Mar 17, 2026
390d97a
add debounced
GrandSchtroumpf Mar 17, 2026
6bc4dec
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 23, 2026
c50cf3b
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Mar 26, 2026
663b5ac
hide slippage when debouncing
GrandSchtroumpf Mar 26, 2026
27e546e
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Apr 13, 2026
b4114cd
mark dex aggregator to false
GrandSchtroumpf Apr 13, 2026
91d7748
[CI] Update Screenshots
GrandSchtroumpf Apr 13, 2026
bf9c4e0
Merge remote-tracking branch 'origin/main' into dex-aggregator
GrandSchtroumpf Apr 13, 2026
fffff49
fix approval for liquidity matrix
GrandSchtroumpf Apr 13, 2026
6268a58
[CI] Update Screenshots
GrandSchtroumpf Apr 13, 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
2 changes: 1 addition & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ CI=false
VITE_TENDERLY_ACCESS_KEY=

# Open Ocean
OPENOCEAN_APIKEY=
DEX_AGGREGATOR_APIKEY=
21 changes: 0 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,27 +395,6 @@ fontFamily: {

You can also remove the unused `@font-face` from the [`src/fonts.css`](src/fonts.css) file.

# Openocean

_Contact Bancor team before setting openocean so we can assist with the required changes._

In order to setup openocean you need to set the config: `ui.useOpenocean` to `true`.
By default Carbon tries calling cloudflare function to call openocean.
If you're not using cloudflare function, check the section [Without Cloudflare functions](#without-cloudflare-functions).

## With cloudflare functions

Under [`functions/api/openocean/index.ts`](/functions/api/openocean/index.ts)

- Update the variable `referrers` & `referrerFee` with your own values.
- Add `OPENOCEAN_APIKEY` in your environment variables in cloudflare.

## Without cloudflare functions

Under [`src/services/openocean.ts`](/src/services/openocean.ts) update the `getUrl` function to send to your own implementation.

Alternatively you can update the `referrer`, `referrerFee` and `apiKey` in the same file in order to do a client call. **But the apiKey would be public** !

# License

The license used is the MIT License. You can find it [here](LICENSE).
5 changes: 4 additions & 1 deletion e2e/pages/trade.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ test.describe('Trade', () => {
await waitForTenderlyRpc(page);

// Token approval
await tokenApproval.checkApproval([source], isLimitedApproval);
await tokenApproval.checkApproval(
[{ symbol: source, amount: sourceValue }],
isLimitedApproval,
);

// Verify form empty
await driver.awaitSuccess();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions e2e/tests/strategy/disposable/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ export const create = (testCase: CreateStrategyTestCase) => {

const tokenApproval = new TokenApprovalDriver(page);
if (direction === 'buy') {
await tokenApproval.checkApproval([quote]);
await tokenApproval.checkApproval([
{ symbol: quote, amount: testCase.input.create.budget },
]);
} else {
await tokenApproval.checkApproval([base]);
await tokenApproval.checkApproval([
{ symbol: base, amount: testCase.input.create.budget },
]);
}

await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
Expand Down
5 changes: 4 additions & 1 deletion e2e/tests/strategy/overlapping/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export const create = (testCase: CreateStrategyTestCase) => {

const tokenApproval = new TokenApprovalDriver(page);
await createForm.submit('create');
await tokenApproval.checkApproval([base, quote]);
await tokenApproval.checkApproval([
{ symbol: base, amount: sell.budget },
{ symbol: quote, amount: buy.budget },
]);
await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
await myStrategies.waitForUpdates();
await waitForTenderlyRpc(page);
Expand Down
8 changes: 7 additions & 1 deletion e2e/tests/strategy/overlapping/deposit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export const deposit = (testCase: CreateStrategyTestCase) => {
await form.budget().fill(input.budget);
await edit.submit('deposit');

await tokenApproval.checkApproval([base, quote]);
const getAmount = (anchor: 'sell' | 'buy') => {
return input.anchor === anchor ? input.budget : '0';
};
await tokenApproval.checkApproval([
{ symbol: base, amount: getAmount('sell') },
{ symbol: quote, amount: getAmount('buy') },
]);
await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
await page.mouse.move(0, 0); // Prevent mouse to open tooltip

Expand Down
10 changes: 9 additions & 1 deletion e2e/tests/strategy/overlapping/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ export const editPrice = (testCase: CreateStrategyTestCase) => {
await form.budget().fill(input.budget);
await edit.submit('editPrices');

await tokenApproval.checkApproval([base, quote]);
const getAmount = (anchor: 'sell' | 'buy') => {
if (input.action !== 'withdraw') return '0';
if (input.anchor !== anchor) return '0';
return input.budget;
};
await tokenApproval.checkApproval([
{ symbol: base, amount: getAmount('sell') },
{ symbol: quote, amount: getAmount('buy') },
]);
await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
await page.mouse.move(0, 0); // Prevent mouse to open tooltip

Expand Down
5 changes: 4 additions & 1 deletion e2e/tests/strategy/overlapping/withdraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export const withdraw = (testCase: CreateStrategyTestCase) => {
await form.budget().fill(input.budget);
await edit.submit('withdraw');

await tokenApproval.checkApproval([base, quote]);
await tokenApproval.checkApproval([
{ symbol: base, amount: '0' },
{ symbol: quote, amount: '0' },
]);
await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
await page.mouse.move(0, 0); // Prevent mouse to open tooltip

Expand Down
6 changes: 5 additions & 1 deletion e2e/tests/strategy/recurring/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { PortfolioDriver } from '../../../utils/strategy/PortfolioDriver';
export const createRecurringStrategy = (testCase: CreateStrategyTestCase) => {
assertRecurringTestCase(testCase);
const { base, quote } = testCase;
const { buy, sell } = testCase.input.create;
const output = testCase.output.create;

return test(`Create`, async ({ page }) => {
Expand All @@ -42,7 +43,10 @@ export const createRecurringStrategy = (testCase: CreateStrategyTestCase) => {

const tokenApproval = new TokenApprovalDriver(page);
await createForm.submit('create');
await tokenApproval.checkApproval([base, quote]);
await tokenApproval.checkApproval([
{ symbol: base, amount: sell.budget },
{ symbol: quote, amount: buy.budget },
]);

await page.waitForURL('/portfolio/strategies', { timeout: 10_000 });
await waitForTenderlyRpc(page);
Expand Down
5 changes: 4 additions & 1 deletion e2e/utils/DebugDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ export class DebugDriver {
.getByTestId('strategy-json-shortcut')
.fill(JSON.stringify(template));
await this.page.getByTestId('create-strategies').click();
await deps.tokenApproval.checkApproval([base, quote]);
await deps.tokenApproval.checkApproval([
{ symbol: base, amount: sell.budget },
{ symbol: quote, amount: buy.budget },
]);
}
}
30 changes: 21 additions & 9 deletions e2e/utils/TokenApprovalDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,36 @@ import { Page, expect } from '@playwright/test';
import { waitModalOpen } from './modal';
import { waitFor } from './operators';

interface Approval {
symbol: string;
amount: string;
}

export class TokenApprovalDriver {
private approvedTokens = ['ETH'];
constructor(private page: Page) {}
async checkApproval(tokens: string[], limitedApproval?: boolean) {
if (tokens.every((token) => this.approvedTokens.includes(token))) return;
async checkApproval(approvals: Approval[], limitedApproval?: boolean) {
const noApprovalNeeded = approvals.every(({ symbol, amount }) => {
if (!Number(amount)) return true;
return this.approvedTokens.includes(symbol);
});
if (noApprovalNeeded) return;
const modal = await waitModalOpen(this.page);
for (const token of tokens) {
if (token === 'ETH') {
const msg = modal.getByTestId(`msg-${token}`);
for (const { symbol, amount } of approvals) {
if (this.approvedTokens.includes(symbol)) {
if (!Number(amount)) continue;
const msg = modal.getByTestId(`msg-${symbol}`);
await expect(msg).toHaveText('Pre-Approved');
} else {
if (limitedApproval)
await modal.getByTestId(`approve-limited-${token}`).click();
await modal.getByTestId(`approve-${token}`).click();
const msg = await waitFor(this.page, `msg-${token}`, 20000);
if (limitedApproval) {
await modal.getByTestId(`approve-limited-${symbol}`).click();
}
await modal.getByTestId(`approve-${symbol}`).click();
const msg = await waitFor(this.page, `msg-${symbol}`, 20000);
await expect(msg).toHaveText('Approved');
}
}
const tokens = approvals.map((a) => a.symbol);
if (!limitedApproval) this.approvedTokens.push(...tokens);
await modal.getByTestId('approve-submit').click();
}
Expand Down
105 changes: 0 additions & 105 deletions functions/api/openocean/index.ts

This file was deleted.

4 changes: 0 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
<meta name="twitter:title" content="Carbon DeFi - Automated trading strategies">
<meta name="twitter:description" content="Carbon DeFi is an advanced onchain trading protocol enabling automated limit orders, efficiently adjustable w/ custom price ranges, grid trading like recurring orders, works like a DEX trading bot.">
<meta name="twitter:image" content="https://framerusercontent.com/images/7YeuJPKg50bkh0m7b1yRwhCNIoE.png">

<!-- Preload fonts that are the most likely to be used quickly -->
<link rel="preload" href="assets/font/title/medium.woff2" as="font" type="font/woff2"/>
<link rel="preload" href="assets/font/text/medium.woff2" as="font" type="font/woff2"/>
</head>

<body>
Expand Down
22 changes: 22 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,30 @@ import { Toaster } from 'components/common/Toaster/Toaster';
import { Footer } from 'components/common/Footer/Footer';
import { SVGCarbonLogo } from 'components/common/SVGCarbonLogo';
import { SVGGradient } from 'components/common/SVGGradient';
import { useWagmi } from 'libs/wagmi';
import { useEffect } from 'react';
import { lsService } from 'services/localeStorage';
import config from 'config';

export const App = () => {
// Keep track of connection
const { user, disconnect } = useWagmi();
useEffect(() => {
if (navigator.webdriver) return;
if (!config.policiesLastUpdated) return;
const last = lsService.getItem('lastConnection');
const lastPrivacy = new Date(config.policiesLastUpdated).getTime();
if (!last || last < lastPrivacy) {
// for some reason disconnect doesn't exist before 1s
setTimeout(() => disconnect(), 1000);
}
}, [disconnect]);
useEffect(() => {
if (user) {
lsService.setItem('lastConnection', Date.now());
}
}, [user]);

return (
<>
<NotificationAlerts />
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/TokenInputField/Slippage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface Props {
const slippageColor = (slippage: SafeDecimal) => {
if (slippage?.gt(0)) return 'text-primary';
if (slippage?.isZero()) return 'text-warning';
if (slippage?.lt(-3)) return 'text-error';
if (slippage?.lt(-3)) return 'text-warning';
return 'text-main-0/80';
};

Expand Down
Loading