Skip to content

Commit

Permalink
Merge pull request #2325 from decentdao/release/v0.3.0
Browse files Browse the repository at this point in the history
Bump the version
  • Loading branch information
adamgall authored Aug 29, 2024
2 parents b84b0e9 + 9ed4c67 commit 5a81b79
Show file tree
Hide file tree
Showing 213 changed files with 30,020 additions and 2,038 deletions.
28 changes: 14 additions & 14 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Minutes to cache token balances for address
BALANCES_CACHE_INTERVAL_MINUTES=""

# Minutes to give Moralis to index new addresses
BALANCES_MORALIS_INDEX_DELAY_MINUTES=""

# Moralis API key
MORALIS_API_KEY=""

Expand All @@ -12,27 +15,24 @@ SENTRY_AUTH_TOKEN=""
# App name displayed as Title
VITE_APP_NAME="Decent"

# Alchemy provider API key, used on Mainnet
VITE_APP_ALCHEMY_MAINNET_API_KEY=""
# Alchemy provider API key, used on Polygon
VITE_APP_ALCHEMY_POLYGON_API_KEY=""
# Alchemy provider API key, used on Sepolia
VITE_APP_ALCHEMY_SEPOLIA_API_KEY=""
# Alchemy provider API key, used on Base Sepolia
VITE_APP_ALCHEMY_BASE_SEPOLIA_API_KEY=""
# Alchemy provider API key, used on Base
VITE_APP_ALCHEMY_BASE_API_KEY=""
# Alchemy provider API key, used on Optimism
VITE_APP_ALCHEMY_OPTIMISM_API_KEY=""
# Hotjar Site ID. This should be parseable to an integer.
VITE_APP_HOTJAR_SITE_ID=""

# Hotjar Version. Should be retrieved from the Hotjar dashboard. This should be parseable to an integer.
VITE_APP_HOTJAR_VERSION=""

# API key for Amplitude analytics
VITE_APP_AMPLITUDE_API_KEY=""

# Alchemy provider API key
VITE_APP_ALCHEMY_API_KEY=""

# ABI selector, used on Mainnet
VITE_APP_ETHERSCAN_MAINNET_API_KEY=""
# ABI selector, used on Polygon
VITE_APP_ETHERSCAN_POLYGON_API_KEY=""
# ABI selector, used on Sepolia
VITE_APP_ETHERSCAN_SEPOLIA_API_KEY=""
# ABI selector, used on Base Sepolia
VITE_APP_ETHERSCAN_BASE_SEPOLIA_API_KEY=""
# ABI selector, used on Base
VITE_APP_ETHERSCAN_BASE_API_KEY=""
# ABI selector, used on Optimism
Expand Down
5 changes: 5 additions & 0 deletions .graphclientrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ sources:
handler:
graphql:
endpoint: https://api.studio.thegraph.com/query/{context.subgraphSpace:71032}/{context.subgraphSlug:fractal-mainnet}/{context.subgraphVersion:v0.1.1}
- name: sablier
handler:
graphql:
endpoint: https://api.studio.thegraph.com/query/{context.subgraphSpace:57079}/{context.subgraphSlug:sablier-v2}/version/latest

documents:
- ./src/graphql/DAO.graphql
- ./src/graphql/Streams.graphql
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20.12.0
20.16.0
38 changes: 30 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@
</head>
<style>
@font-face {
font-family: 'TT Firs Neue Variable';
font-family: 'DM Sans Variable';
src:
url('/fonts/TT_Firs_Neue_Variable.woff') format('woff'),
url('/fonts/TT_Firs_Neue_Variable.woff2') format('woff2');
url('/fonts/DMSans-VariableFont_opsz,wght.woff2') format('woff2'),
url('/fonts/DMSans-VariableFont_opsz,wght.woff') format('woff');
}
</style>
<body>
Expand Down
4 changes: 4 additions & 0 deletions netlify.toml
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
38 changes: 38 additions & 0 deletions netlify/functions/defiBalances.mts
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);
}
110 changes: 21 additions & 89 deletions netlify/functions/nftBalances.mts
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);
}
Loading

0 comments on commit 5a81b79

Please sign in to comment.