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

feat: 85 update swap page logic to cosmjs usage #93

Merged
merged 16 commits into from
Jul 25, 2022
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
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ REACT_APP__APP_VERSION=$npm_package_version

# Contract Info
REACT_APP__CHAIN_ID=31337
REACT_APP__CONTRACT_ADDRESS__DUALITY_CORE=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
REACT_APP__CONTRACT_ADDRESS__DUALITY_CORE=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
REACT_APP__REST_API=http://localhost:1317
REACT_APP__WEBSOCKET_URL=ws://localhost:26657/websocket
2 changes: 2 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
REACT_APP__CHAIN_ID=31337
REACT_APP__CONTRACT_ADDRESS__DUALITY_CORE=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
REACT_APP__REST_API=http://localhost:1317
REACT_APP__WEBSOCKET_URL=ws://localhost:26657/websocket
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
REACT_APP__CHAIN_ID=31337
REACT_APP__CONTRACT_ADDRESS__DUALITY_CORE=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9
REACT_APP__REST_API=http://localhost:1317
REACT_APP__WEBSOCKET_URL=ws://localhost:26657/websocket
29 changes: 14 additions & 15 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dependencies": {
"@floating-ui/react-dom": "^0.6.3",
"@reach/dialog": "^0.17.0",
"bignumber.js": "^9.0.2",
"buffer": "^6.0.3",
"ethers": "^5.6.4",
"invariant": "^2.2.4",
Expand Down Expand Up @@ -75,4 +76,4 @@
"last 1 safari version"
]
}
}
}
4 changes: 2 additions & 2 deletions src/components/TokenPicker/mockHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const tokens: Array<Token> = [
logo: null,
symbol: 'Dai',
name: 'Dai Stablecoin',
address: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
address: '0x0002',
},
{
logo: null,
symbol: 'USDC',
name: 'USDCoin',
address: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
address: '0x0003',
},
{ logo: null, symbol: 'USDT', name: 'Tether USD', address: '0x0004' },
{ logo: null, symbol: 'WBTC', name: 'Wrapped BTC', address: '0x0005' },
Expand Down
235 changes: 235 additions & 0 deletions src/lib/web3/indexerProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { useContext, createContext, useState, useEffect } from 'react';
import {
EventType,
createSubscriptionManager,
MessageActionEvent,
} from './events';
import { BigNumber } from 'bignumber.js';

const { REACT_APP__REST_API, REACT_APP__WEBSOCKET_URL } = process.env;

type TokenAddress = string; // a valid hex address, eg. 0x01
type BigNumberString = string; // a number in string format, eg. "1"

if (!REACT_APP__WEBSOCKET_URL)
throw new Error('Invalid value for env variable REACT_APP__WEBSOCKET_URL');
const subscriber = createSubscriptionManager(REACT_APP__WEBSOCKET_URL);

export interface PairInfo {
token0: string;
token1: string;
ticks: { [tickID: string]: TickInfo };
}

export interface TickInfo {
price0: BigNumber;
price1: BigNumber;
reserves0: BigNumber;
reserves1: BigNumber;
fee: BigNumber;
}

export interface PairMap {
[pairID: string]: PairInfo;
}

interface IndexerContextType {
data?: PairMap;
error?: string;
isValidating: boolean;
}

const IndexerContext = createContext<IndexerContextType>({
isValidating: true,
});

function getFullData(): Promise<PairMap> {
return new Promise(function (resolve, reject) {
if (!REACT_APP__REST_API) {
reject(new Error('Undefined rest api base URL'));
} else {
fetch(`${REACT_APP__REST_API}/duality/duality/ticks`)
.then((res) => res.json())
.then(transformData)
.then(resolve)
.catch(reject);
}
});
}

/**
* Gets the pair id for a sorted pair of tokens
* @param token0 address of token 0
* @param token1 address of token 1
* @returns pair id for tokens
*/
function getPairID(token0: TokenAddress, token1: TokenAddress) {
return `${token0}-${token1}`;
}

/**
* Gets the tick id
* @param price0 price of token 0
* @param price1 price of token 1
* @param fee tick's fee
* @returns tick id
*/
function getTickID(
price0: BigNumberString,
price1: BigNumberString,
fee: BigNumberString
) {
return `${price0}-${price1}-${fee}`;
}

function transformData(data: {
tick: Array<{
token0: TokenAddress;
token1: TokenAddress;
price0: string;
price1: string;
fee: string;
reserves0: string;
reserves1: string;
}>;
}): PairMap {
return data.tick.reduce<PairMap>(function (
result,
{ token0, token1, price0, price1, fee, reserves0, reserves1 }
) {
const pairID = getPairID(token0, token1);
const tickID = getTickID(price0, price1, fee);
result[pairID] = result[pairID] || {
token0: token0,
token1: token1,
ticks: {},
};
result[pairID].ticks[tickID] = {
price0: new BigNumber(price0),
price1: new BigNumber(price1),
reserves0: new BigNumber(reserves0),
reserves1: new BigNumber(reserves1),
fee: new BigNumber(fee),
};
return result;
},
{});
}

export function IndexerProvider({ children }: { children: React.ReactNode }) {
const [indexerData, setIndexerData] = useState<PairMap>();
const [error, setError] = useState<string>();
// avoid sending more than once
const [, setRequestedFlag] = useState(false);
const [result, setResult] = useState<IndexerContextType>({
data: indexerData,
error: error,
isValidating: true,
});

useEffect(() => {
const onTickChange = function (event: MessageActionEvent) {
const {
Token0,
Token1,
NewReserves0,
NewReserves1,
Price0,
Price1,
Fee,
} = event;
if (
!Token0 ||
!Token1 ||
!Price0 ||
!Price1 ||
!NewReserves0 ||
!NewReserves1 ||
!Fee
) {
setError('Invalid event response from server');
return;
} else {
setError(undefined);
}
dib542 marked this conversation as resolved.
Show resolved Hide resolved
const pairID = getPairID(Token0, Token1);
const tickID = getTickID(Price0, Price1, Fee);
setIndexerData((oldData = {}) => {
const oldPairInfo = oldData[pairID];
const oldTickInfo = oldPairInfo?.ticks?.[tickID];
return {
...oldData,
[pairID]: {
...oldPairInfo, // not needed, displayed for consistency
token0: Token0,
token1: Token1,
ticks: {
...oldPairInfo?.ticks,
[tickID]: {
...oldTickInfo, // not needed, displayed for consistency
price0: new BigNumber(Price0),
price1: new BigNumber(Price1),
fee: new BigNumber(Fee),
reserves0: new BigNumber(NewReserves0),
reserves1: new BigNumber(NewReserves1),
},
},
},
};
});
};
subscriber.subscribeMessage(onTickChange, EventType.EventTxValue, {
messageAction: 'NewDeposit',
});
subscriber.subscribeMessage(onTickChange, EventType.EventTxValue, {
messageAction: 'NewWithdraw',
});
return () => {
subscriber.unsubscribeMessage(onTickChange);
};
}, []);

useEffect(() => {
setResult({
data: indexerData,
error: error,
isValidating: !indexerData && !error,
});
}, [indexerData, error]);

useEffect(() => {
setRequestedFlag((oldValue) => {
if (oldValue) return true;
getFullData()
.then(function (res) {
setIndexerData(res);
})
.catch(function (err: Error) {
setError(err?.message ?? 'Unknown Error');
});
return true;
});
}, []);

return (
<IndexerContext.Provider value={result}>{children}</IndexerContext.Provider>
);
}

export function useIndexerData() {
return useContext(IndexerContext);
}

export function useIndexerPairData(
tokenA?: TokenAddress,
tokenB?: TokenAddress
) {
const { data: pairs, isValidating, error } = useIndexerData();
const [token0, token1] = [tokenA, tokenB].sort();
const pairID = token0 && token1 && getPairID(token0, token1);
return {
data: pairID && pairs?.[pairID],
error,
isValidating,
};
}
25 changes: 14 additions & 11 deletions src/pages/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Web3Provider } from '../../lib/web3/useWeb3';
import { IndexerProvider } from '../../lib/web3/indexerProvider';

import { BrowserRouter, Routes, Route } from 'react-router-dom';

Expand All @@ -11,17 +12,19 @@ import './App.scss';
function App() {
return (
<Web3Provider>
<BrowserRouter>
<Header />
<main>
<Routes>
<Route index element={<div>Home</div>} />
<Route path="swap" element={<Swap />} />
<Route path="pool" element={<Pool />} />
<Route path="*" element={<div>Not found</div>} />
</Routes>
</main>
</BrowserRouter>
<IndexerProvider>
<BrowserRouter>
<Header />
<main>
<Routes>
<Route index element={<div>Home</div>} />
<Route path="swap" element={<Swap />} />
<Route path="pool" element={<Pool />} />
<Route path="*" element={<div>Not found</div>} />
</Routes>
</main>
</BrowserRouter>
</IndexerProvider>
</Web3Provider>
);
}
Expand Down
Loading