-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2325 from decentdao/release/v0.3.0
Bump the version
- Loading branch information
Showing
213 changed files
with
30,020 additions
and
2,038 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
20.12.0 | ||
20.16.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,10 +5,10 @@ | |
Clone the repository | ||
|
||
```shell | ||
$ git clone {repository url} | ||
$ git clone [email protected]:decentdao/decent-interface.git | ||
``` | ||
|
||
Change to correct Node.js version | ||
Change to application's `Node.js` version | ||
|
||
```shell | ||
$ nvm use | ||
|
@@ -20,23 +20,45 @@ Install the dependencies | |
$ npm install | ||
``` | ||
|
||
Build Subgraph artifacts | ||
Running development environment (without `Netlify` functions) | ||
|
||
```shell | ||
$ npm run graphql:build # For UNIX | ||
$ npm run dev | ||
``` | ||
|
||
Running development environment | ||
Running development environment (with `Netlify` functions) | ||
|
||
```shell | ||
$ npm run dev | ||
$ npm run dev:netlify | ||
``` | ||
|
||
### Netlify functions | ||
|
||
We're using `Netlify` functions for retrieving various off-chain data. | ||
Currently it's being used to fetch abstract `address`'s ERC-20, ERC-721 and DeFi balances through `Moralis`. | ||
It is crucial to have `Netlify` functions running locally to work with anything related to DAO treasury, for instance | ||
|
||
- Treasury page | ||
- Payments feature | ||
|
||
## Subgraph | ||
|
||
We're using `Subgraph` to index certain "metadata" events to simplify data fetching from application site. | ||
Repository, that implements mapping located [here](https://github.com/decentdao/decent-subgraph). | ||
|
||
If you updated mapping and deployed new version - you might need to rebuild `Subgraph` artifacts. Use command below. | ||
|
||
Build Subgraph artifacts | ||
|
||
```shell | ||
$ npm run graphql:build | ||
``` | ||
|
||
## Deployment Notes | ||
|
||
The "dev" and "prod" environments of this app are currently deployed via Netlify. | ||
The "dev" and "prod" environments of this app are currently deployed via `Netlify`. | ||
|
||
The "dev" environment tracks the `develop` branch, and the "prod" environment tracks the `main` branch. | ||
The "dev" environment tracks the `develop` [branch](https://github.com/decentdao/decent-interface/tree/develop), and the "prod" environment tracks the `main` [branch](https://github.com/decentdao/decent-interface/tree/main). | ||
|
||
- dev: https://app.dev.decentdao.org | ||
- prod: https://app.decentdao.org |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[dev] | ||
command = "vite --force" # This starts your Vite server | ||
targetPort = 3000 # Vite server should run on this port | ||
port = 8888 # Netlify Dev will proxy this port |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import type { Store } from '@netlify/blobs'; | ||
import Moralis from 'moralis'; | ||
import type { Address } from 'viem'; | ||
import type { DefiBalance } from '../../src/types'; | ||
import { camelCaseKeys } from '../../src/utils/dataFormatter'; | ||
import { BalanceDataWithMetadata, getBalances } from '../shared/moralisBalances.mts'; | ||
|
||
export default async function getDefiBalancesWithPrices(request: Request) { | ||
const fetchFromStore = async (store: Store, storeKey: string) => { | ||
return store.getWithMetadata(storeKey, { | ||
type: 'json', | ||
}) as Promise<BalanceDataWithMetadata<DefiBalance>> | null; | ||
}; | ||
|
||
const fetchFromMoralis = async (scope: { chain: string; address: Address }) => { | ||
const defiResponse = await Moralis.EvmApi.wallets.getDefiPositionsSummary(scope); | ||
|
||
const mappedDefiData = defiResponse.result | ||
.filter(defiBalance => !!defiBalance.position?.balanceUsd) | ||
.map(defiBalance => { | ||
const balanceJSON = camelCaseKeys(defiBalance.toJSON()) as unknown as DefiBalance; | ||
const mappedBalance = { | ||
...balanceJSON, | ||
position: balanceJSON.position | ||
? { | ||
...camelCaseKeys(balanceJSON.position), | ||
tokens: balanceJSON.position.tokens.map(token => camelCaseKeys(token)), | ||
} | ||
: undefined, | ||
}; | ||
return mappedBalance; | ||
}); | ||
|
||
return mappedDefiData; | ||
}; | ||
|
||
return getBalances(request, 'defi', fetchFromStore, fetchFromMoralis); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,100 +1,32 @@ | ||
import { getStore } from '@netlify/blobs'; | ||
import type { Store } from '@netlify/blobs'; | ||
import Moralis from 'moralis'; | ||
import { isAddress } from 'viem'; | ||
import { moralisSupportedChainIds } from '../../src/providers/NetworkConfig/NetworkConfigProvider'; | ||
import type { Address } from 'viem'; | ||
import type { NFTBalance } from '../../src/types'; | ||
import { camelCaseKeys } from '../../src/utils/dataFormatter'; | ||
|
||
type NFTBalancesWithMetadata = { | ||
data: NFTBalance[]; | ||
metadata: { | ||
fetched: number; | ||
}; | ||
}; | ||
import { BalanceDataWithMetadata, getBalances } from '../shared/moralisBalances.mts'; | ||
|
||
export default async function getNftBalances(request: Request) { | ||
if (!process.env.MORALIS_API_KEY) { | ||
console.error('Moralis API key is missing'); | ||
return Response.json({ error: 'Error while fetching token balances' }, { status: 503 }); | ||
} | ||
|
||
if (!process.env.BALANCES_CACHE_INTERVAL_MINUTES) { | ||
console.error('BALANCES_CACHE_INTERVAL_MINUTES is not set'); | ||
return Response.json({ error: 'Error while fetching prices' }, { status: 503 }); | ||
} | ||
|
||
const requestSearchParams = new URL(request.url).searchParams; | ||
const addressParam = requestSearchParams.get('address'); | ||
|
||
if (!addressParam) { | ||
return Response.json({ error: 'Address missing from request' }, { status: 400 }); | ||
} | ||
|
||
if (!isAddress(addressParam)) { | ||
return Response.json({ error: 'Provided address is not a valid address' }, { status: 400 }); | ||
} | ||
|
||
const networkParam = requestSearchParams.get('network'); | ||
if (!networkParam) { | ||
return Response.json({ error: 'Network missing from request' }, { status: 400 }); | ||
} | ||
|
||
const chainId = parseInt(networkParam); | ||
if (!moralisSupportedChainIds.includes(chainId)) { | ||
return Response.json({ error: 'Requested network is not supported' }, { status: 400 }); | ||
} | ||
|
||
const nftsStore = getStore(`moralis-balances-nfts-${networkParam}`); | ||
const nowSeconds = Math.floor(Date.now() / 1000); | ||
const cacheTimeSeconds = parseInt(process.env.BALANCES_CACHE_INTERVAL_MINUTES) * 60; | ||
const config = { nowSeconds, cacheTimeSeconds }; | ||
const storeKey = `${networkParam}/${addressParam}`; | ||
try { | ||
const balances = await (nftsStore.getWithMetadata(storeKey, { | ||
const fetchFromStore = async (store: Store, storeKey: string) => { | ||
return store.getWithMetadata(storeKey, { | ||
type: 'json', | ||
}) as Promise<NFTBalancesWithMetadata> | null); | ||
}) as Promise<BalanceDataWithMetadata<NFTBalance>> | null; | ||
}; | ||
|
||
if ( | ||
balances?.metadata.fetched && | ||
balances.metadata.fetched + config.cacheTimeSeconds > config.nowSeconds | ||
) { | ||
return Response.json({ data: balances.data }); | ||
} else { | ||
if (!Moralis.Core.isStarted) { | ||
await Moralis.start({ | ||
apiKey: process.env.MORALIS_API_KEY, | ||
}); | ||
} | ||
let mappedNftsData: NFTBalance[] = []; | ||
const fetchFromMoralis = async (scope: { chain: string; address: Address }) => { | ||
const nftsResponse = await Moralis.EvmApi.nft.getWalletNFTs(scope); | ||
|
||
let nftsFetched = false; | ||
try { | ||
const nftsResponse = await Moralis.EvmApi.nft.getWalletNFTs({ | ||
chain: chainId.toString(), | ||
address: addressParam, | ||
}); | ||
mappedNftsData = nftsResponse.result.map(nftBalance => | ||
camelCaseKeys<ReturnType<typeof nftBalance.toJSON>>(nftBalance.toJSON()), | ||
); | ||
nftsFetched = true; | ||
} catch (e) { | ||
console.error('Error while fetching address NFTs', e); | ||
nftsFetched = false; | ||
} | ||
const mappedNftsData = nftsResponse.result.map(nftBalance => { | ||
const balanceJSON = camelCaseKeys<ReturnType<typeof nftBalance.toJSON>>( | ||
nftBalance.toJSON(), | ||
) as unknown as NFTBalance; | ||
return { | ||
...balanceJSON, | ||
metadata: balanceJSON.metadata ? camelCaseKeys(balanceJSON.metadata) : undefined, | ||
}; | ||
}); | ||
|
||
if (nftsFetched) { | ||
await nftsStore.setJSON(storeKey, mappedNftsData, { | ||
metadata: { fetched: config.nowSeconds }, | ||
}); | ||
} | ||
return mappedNftsData; | ||
}; | ||
|
||
return Response.json({ data: mappedNftsData }); | ||
} | ||
} catch (e) { | ||
console.error(e); | ||
return Response.json( | ||
{ error: 'Unexpected error while fetching NFTs balances' }, | ||
{ status: 503 }, | ||
); | ||
} | ||
return getBalances(request, 'nfts', fetchFromStore, fetchFromMoralis); | ||
} |
Oops, something went wrong.