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

chore(explorer): migrate pending PRs from old repo #3584

Merged
merged 22 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6dadb0c
chore: setup blank react project
shoom3301 Jan 3, 2024
7734964
chore: copy-paste explorer files
shoom3301 Jan 3, 2024
86b3c1b
chore: copy-paste test files
shoom3301 Jan 3, 2024
18741d1
feat(explorer): update project and migrate to vite
shoom3301 Jan 8, 2024
5c14e8f
feat: env var for custom order-book API urls
shoom3301 Jan 8, 2024
602cc77
chore: remove commented out IPFS related code
shoom3301 Jan 8, 2024
f3de188
chore: remove appId
shoom3301 Jan 8, 2024
e42f873
chore: remove unnecessary code
shoom3301 Jan 8, 2024
1451406
chore: update yarn lock
shoom3301 Jan 8, 2024
2d9a492
chore: remove todos and fix 404 page
shoom3301 Jan 9, 2024
3280aba
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 9, 2024
a9a2f8d
chore: remove irrelevant test
shoom3301 Jan 9, 2024
9020d56
chore: fix lint
shoom3301 Jan 9, 2024
f0228b3
chore: remove outdated files
shoom3301 Jan 9, 2024
20a61d5
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
a789b58
chore: test commit
shoom3301 Jan 9, 2024
3048211
chore: test commit
shoom3301 Jan 9, 2024
53c3578
chore: fix dynamic import
shoom3301 Jan 9, 2024
4769540
Merge branch 'feat/explorer-3' of https://github.com/cowprotocol/cows…
shoom3301 Jan 9, 2024
e22e13e
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 10, 2024
454486a
Merge branch 'develop' of https://github.com/cowprotocol/cowswap into…
shoom3301 Jan 11, 2024
6cb9170
chore: fix docs
shoom3301 Jan 11, 2024
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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ yarn build
yarn test
```

# 🔎 Explorer

Start the Explorer on http://localhost:4200

[Read more about the Explorer](apps/explorer/README.md)

### Start
```bash
yarn start:explorer
```

### Build
```bash
yarn build:explorer
```


# 🖼️ Widget Configurator

Start the Widget Configurator on http://127.0.0.1:4200/widget-configurator
Expand Down
16 changes: 16 additions & 0 deletions apps/explorer/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Enables mock mode (default = true)
MOCK=true

# Enables autoconnect for mock mode (default = true)
AUTOCONNECT=true

# Public IPFS gateway
REACT_APP_IPFS_READ_URI=https://cloudflare-ipfs.com/ipfs
shoom3301 marked this conversation as resolved.
Show resolved Hide resolved

# Sentry
#REACT_APP_SENTRY_DSN='https://<url>'
#REACT_APP_SENTRY_TRACES_SAMPLE_RATE="1.0"
#REACT_APP_SENTRY_AUTH_TOKEN='<sentry_auth_token>'

# Orderbook API Endpoints
#REACT_APP_ORDER_BOOK_URLS='{"1":"https://YOUR_HOST","100":"https://YOUR_HOST","5":"https://YOUR_HOST"}
35 changes: 35 additions & 0 deletions apps/explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Explorer
shoom3301 marked this conversation as resolved.
Show resolved Hide resolved

## 🏃‍♀️ Run it locally

```bash
yarn start:explorer
```


## 👷‍♀️ Build app

```bash
yarn build:explorer
```

Static files will be generated inside the `build/explorer` dir.

## 🧪 Run tests

```bash
yarn test:explorer
```

## Orderbook API Endpoints

Fee quote requests and posting orders are sent to the Orderbook API. This API has the responsibility of collecting orders and
handing them to the solvers.

The reference implementation of the API is [CoW Protocol Services](https://github.com/cowprotocol/services).

The API endpoint is configured using the environment variable `REACT_APP_ORDER_BOOK_URLS`:

```ini
REACT_APP_ORDER_BOOK_URLS={"1":"https://YOUR_HOST","100":"https://YOUR_HOST","5":"https://YOUR_HOST"}
```
1 change: 0 additions & 1 deletion apps/explorer/src/api/operator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const useMock = process.env.MOCK_OPERATOR === 'true'
export const {
// functions that have a mock
getOrder,
getOrders,
getAccountOrders,
getTxOrders,
getTrades,
Expand Down
97 changes: 1 addition & 96 deletions apps/explorer/src/api/operator/operatorApi.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,9 @@
import { Network } from 'types'
import { buildSearchString } from 'utils/url'
import { isProd, isStaging } from 'utils/env'

import { GetOrderParams, GetOrdersParams, RawOrder, RawTrade, GetTxOrdersParams, WithNetworkId } from './types'
import { fetchQuery } from 'api/baseApi'
import { GetOrderParams, RawOrder, RawTrade, GetTxOrdersParams, WithNetworkId } from './types'
import { orderBookSDK } from 'cowSdk'
import { Address, UID } from '@cowprotocol/cow-sdk'

export { getAccountOrders } from './accountOrderUtils'

// TODO: export this through the sdk
export type ApiEnv = 'prod' | 'staging'

// TODO: should come from the SDK by now, find out where this is used
function getOperatorUrl(): Partial<Record<Network, string>> {
if (isProd || isStaging) {
return {
[Network.MAINNET]: process.env.OPERATOR_URL_PROD_MAINNET,
[Network.GOERLI]: process.env.OPERATOR_URL_PROD_GOERLI,
[Network.GNOSIS_CHAIN]: process.env.OPERATOR_URL_PROD_XDAI,
}
} else {
return {
[Network.MAINNET]: process.env.OPERATOR_URL_STAGING_MAINNET,
[Network.GOERLI]: process.env.OPERATOR_URL_STAGING_GOERLI,
[Network.GNOSIS_CHAIN]: process.env.OPERATOR_URL_STAGING_XDAI,
}
}
}

const API_BASE_URL = getOperatorUrl()

const DEFAULT_HEADERS: Headers = new Headers({
'Content-Type': 'application/json',
})

/**
* Unique identifier for the order, calculated by keccak256(orderDigest, ownerAddress, validTo),
where orderDigest = keccak256(orderStruct). bytes32.
*/

function _getApiBaseUrl(networkId: Network): string {
const baseUrl = API_BASE_URL[networkId]

if (!baseUrl) {
throw new Error('Unsupported Network. The operator API is not deployed in the Network ' + networkId)
} else {
return baseUrl + '/v1'
}
}

function _get(networkId: Network, url: string): Promise<Response> {
const baseUrl = _getApiBaseUrl(networkId)
return fetch(baseUrl + url, {
headers: DEFAULT_HEADERS,
})
}

/**
* Gets a single order by id
*/
Expand All @@ -66,37 +13,6 @@ export async function getOrder(params: GetOrderParams): Promise<RawOrder | null>
return orderBookSDK.getOrderMultiEnv(orderId, { chainId: networkId })
}

/**
* Gets a list of orders
*
* Optional filters:
* - owner: address
* - sellToken: address
* - buyToken: address
* - minValidTo: number
*/
export async function getOrders(params: GetOrdersParams): Promise<RawOrder[]> {
const { networkId, ...searchParams } = params
const { owner, sellToken, buyToken, minValidTo } = searchParams
const defaultValues = {
includeFullyExecuted: 'true',
includeInvalidated: 'true',
includeInsufficientBalance: 'true',
includePresignaturePending: 'true',
includeUnsupportedTokens: 'true',
}

console.log(
`[getOrders] Fetching orders on network ${networkId} with filters: owner=${owner} sellToken=${sellToken} buyToken=${buyToken}`
)

const searchString = buildSearchString({ ...searchParams, ...defaultValues, minValidTo: String(minValidTo) })

const queryString = '/orders/' + searchString

return _fetchQuery(networkId, queryString)
}

/**
* Gets a order list within Tx
*/
Expand Down Expand Up @@ -148,14 +64,3 @@ export async function getTrades(

return [...trades[0], ...trades[1]]
}

function _fetchQuery<T>(networkId: Network, queryString: string): Promise<T>
function _fetchQuery<T>(networkId: Network, queryString: string, nullOn404: true): Promise<T | null>
function _fetchQuery<T>(networkId: Network, queryString: string, nullOn404?: boolean): Promise<T | null> {
const get = (): Promise<Response> => _get(networkId, queryString)
if (nullOn404) {
return fetchQuery({ get }, queryString, nullOn404)
}

return fetchQuery({ get }, queryString)
}
9 changes: 8 additions & 1 deletion apps/explorer/src/cowSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { OrderBookApi, SubgraphApi } from '@cowprotocol/cow-sdk'
import { MetadataApi } from '@cowprotocol/app-data'
import { SUBGRAPH_URLS } from './consts/subgraphUrls'

export const orderBookSDK = new OrderBookApi({ env: 'prod' })
const prodBaseUrls = process.env.REACT_APP_ORDER_BOOK_URLS
? JSON.parse(process.env.REACT_APP_ORDER_BOOK_URLS)
: undefined

export const orderBookSDK = new OrderBookApi({
env: 'prod',
...(prodBaseUrls ? { baseUrls: prodBaseUrls } : undefined),
})
export const subgraphApiSDK = new SubgraphApi({
baseUrls: SUBGRAPH_URLS,
})
Expand Down
120 changes: 1 addition & 119 deletions apps/explorer/src/explorer/pages/AppData/EncodePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const EncodePage: React.FC<EncodeProps> = ({ tabData, setTabData /* handleTabCha
useEffect(() => {
setIsLoading(true)

// Get the fullAppData (dermenistic stringify JSON)
// Get the fullAppData (deterministic stringify JSON)
_toFullAppData(appDataForm)
.then((fullAppData) => {
// Update the fullAppData
Expand Down Expand Up @@ -133,11 +133,6 @@ const EncodePage: React.FC<EncodeProps> = ({ tabData, setTabData /* handleTabCha
[]
)

// const handleIPFSErrors = useCallback(
// (_: FormProps, errors: FormValidation): FormValidation => handleErrors(ipfsFormRef, errors, setDisabledIPFS),
// [],
// )

const handleOnChange = useCallback(
({ formData }: FormProps): void => {
const resetFormFields = (form: string): void => {
Expand All @@ -157,34 +152,6 @@ const EncodePage: React.FC<EncodeProps> = ({ tabData, setTabData /* handleTabCha
[ipfsHashInfo]
)

// const handleIPFSOnChange = useCallback(({ formData: ipfsData }: FormProps): void => {
// setIpfsCredentials(ipfsData)
// if (JSON.stringify(ipfsData) !== JSON.stringify({})) {
// setDisabledIPFS(false)
// }
// }, [])

// const onUploadToIPFS = useCallback(
// async ({ formData }: FormProps): Promise<void> => {
// if (!ipfsHashInfo) return
// setIsLoading(true)
// try {
// await metadataApiSDK.uploadMetadataDocToIpfsLegacy(handleFormatData(appDataForm), formData)
// setIsDocUploaded(true)
// } catch (e) {
// if (INVALID_IPFS_CREDENTIALS.includes(e.message)) {
// e.message = 'Invalid API keys provided.'
// }
// setError(e.message)
// setIsDocUploaded(false)
// } finally {
// setIsLoading(false)
// toggleInvalid({ ipfs: true })
// }
// },
// [appDataForm, ipfsHashInfo],
// )

return (
<>
<div className="info-header box">
Expand Down Expand Up @@ -319,91 +286,6 @@ const EncodePage: React.FC<EncodeProps> = ({ tabData, setTabData /* handleTabCha
</div>
</AppDataWrapper>
</div>
{/* <div className="ipfs-container">
{ipfsHashInfo && (
<>
<IpfsWrapper>
<div className="info-header inner-form">
<h2>IPFS UPLOAD</h2>
<p>
We offer an integrated way to upload the generated file to IPFS directly through
<a target="_blank" href="https://docs.pinata.cloud/pinata-api" rel="noreferrer">
Pinata’s
</a>
API.
</p>
<p>
Insert your credentials and hit upload. The resulting <strong>appData</strong> hash is displayed on
the generated section.
</p>
<p>
Once uploaded, you can use the <strong>appData</strong> hash in the
<a onClick={(): void => handleTabChange(TabView.DECODE)}>Decode</a>
tab to verify it.
</p>
</div>
<Form
className="data-form"
showErrorList={false}
onSubmit={onUploadToIPFS}
liveValidate={invalidFormDataAttempted.ipfs}
onChange={handleIPFSOnChange}
formData={ipfsCredentials}
validate={handleIPFSErrors}
fields={{ cField: CustomField }}
ref={ipfsFormRef}
noHtml5Validate
onError={(): void => toggleInvalid({ ipfs: true })}
transformErrors={transformErrors}
schema={ipfsSchema}
uiSchema={ipfsUiSchema}
>
<span className="disclaimer">
<strong>Notes:</strong>
<ol>
<li>
The credentials are kept in memory only while you are at this page. When you navigate away or
close the browser tab it’ll be cleared.
</li>
<li>
If you upload the file directly, the resulting hash/appDataHash might differ. The hash/IPFS CID
calculated by the tool is a minified and stringified file without a new line at the end. That
means that you will get different results if the file is uploaded directly as a file.
</li>
</ol>
</span>
<button className="btn btn-info" disabled={disabledIPFS} type="submit">
UPLOAD APPDATA TO IPFS
</button>
</Form>
</IpfsWrapper>
</>
)}
{isDocUploaded && (
<>
<RowWithCopyButton
className="appData-hash"
textToCopy={`${DEFAULT_IPFS_READ_URI}/${ipfsHashInfo?.cid}`}
contentsToDisplay={
<a href={`${DEFAULT_IPFS_READ_URI}/${ipfsHashInfo?.cid}`} target="_blank" rel="noopener noreferrer">
{ipfsHashInfo?.cid}
</a>
}
/>
<Notification
type="success"
message="Document uploaded successfully!"
closable={false}
appendMessage={false}
/>
</>
)}
{isLoading && <Spinner />}
{error && !isDocUploaded && (
<Notification type="error" message={error} closable={false} appendMessage={false} />
)}
</div>
*/}
</>
)
}
Expand Down
Loading
Loading