Skip to content

Commit

Permalink
Change default tokenlist to custom one
Browse files Browse the repository at this point in the history
  • Loading branch information
sembrestels committed Jul 9, 2024
1 parent ee98955 commit 2feb331
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 4 deletions.
Binary file modified bun.lockb
Binary file not shown.
11 changes: 7 additions & 4 deletions packages/evmcrispr/src/modules/std/helpers/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import {
} from "../../../utils";
import type { Module } from "../../../Module";
import type { Std } from "../Std";
import type { BindingsManager } from "../../../BindingsManager";

const ENV_TOKENLIST = "$token.tokenlist";
const DEFAULT_TOKEN_LIST = "https://tokens.uniswap.org/";

const getTokenList = ({ bindingsManager }: Module): string => {
const getTokenList = async (
bindingsManager: BindingsManager,
chainId: number,
): Promise<string> => {
const tokenList = String(
bindingsManager.getBindingValue(ENV_TOKENLIST, BindingsSpace.USER) ??
DEFAULT_TOKEN_LIST,
`https://tokens.functions.on-fleek.app/v0/${chainId}`,
);

// Always check user data inputs:
Expand All @@ -41,7 +44,7 @@ export const _token = async (
return tokenSymbolOrAddress;
}
const chainId = await module.getChainId();
const tokenList = getTokenList(module);
const tokenList = await getTokenList(module.bindingsManager, chainId);
const {
tokens,
}: { tokens: { symbol: string; chainId: number; address: string }[] } =
Expand Down
88 changes: 88 additions & 0 deletions packages/token-list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Fleek Image Optimizer

## Overview

Fleek Image Optimizer is a tool designed to enhance images for web delivery. It supports various image formats and provides functionalities to resize and re-encode images into more efficient formats like AVIF and WebP.

## Similar (propertary) services
These are all services that allow image optimization among other features.
- [Cloudinary](https://cloudinary.com/developers)
- [Imagekit](https://imagekit.io/docs/overview)
- [Imgix](https://www.imgix.com/solutions/compression-and-performance)
- [Akami](https://techdocs.akamai.com/ivm/docs/optimize-images)

## Optimizing images in Fleek

A very interesting resource for image optimization that uses WebAssembly and browser technologies is [Squoosh.app](https://squoosh.app) from Google Chrome Labs. The app [repo](https://github.com/GoogleChromeLabs/squoosh) is open source and up to date with support for the latest file formats.

For anyone who want to use the wasm modules underneath, there is an unofficial set of npm packages under [@jsquash/\*](https://github.com/jamsinclair/jSquash) that include the WASM modules with support for encoding, resizing, and decoding avif, jpeg, jxl, png and webp.

In order to make WASM work on Fleek Functions, I needed to configure the rollup bundler in a specific way to inline dynamic imports and WASM modules. I created an independent repo so anyone who needs to use WASM in Fleek can find how: [Fleek Functions WebAssembly Starter Kit](https://github.com/BlossomLabs/fleek-function-wasm-starter).

Using that configuration, I could import the wasm modules from jsquash, and use them in a fleek function that receives the image URI and the desired width, and converts it to the most efficient image type that is available for the requesting browser.

### Usage

You can call the function by passing the image URL and optional `width` and `to` parameters to the query string like this:

```
https://image-optimizer.functions.on-fleek.app/<url>/?w=<width>&to=<type>
```

If the `w` parameter is not provided, it will deliver the image size as is.

If the `to` parameter is not provided, it will check the image types accepted by the client and deliver a format that fits, prioritizing AVIF, and then WebP.

**Example:**

```
https://image-optimizer.functions.on-fleek.app/https://fleek.network/share-image.png/?w=500
```

It converts a 260 kB PNG image to a 5.21 kB WEBP image and delivers it as a response that takes less than a second.

## Installation and Usage

### Installation

First, install the required dependencies using:

```sh
bun i
```

### Building the Project

To build the project, use the following command:

```sh
bun run build
```

This will create a `./dist/main.js` bundle that includes all tree-shaken dependencies and inline WASM modules.

### Deployment

To deploy the project, follow these steps:

1. Create a function:

```sh
bun create-func $NAME
```

2. Deploy the function:

```sh
bun deploy-func $NAME
```

Replace `$NAME` with the desired name of your function.

## Contributing

We welcome contributions! If you have suggestions for improvements or encounter any issues, please open an issue or submit a pull request on our GitHub repository.

## Contact

This project was developed as part of the Fleek Hacker House at ETH Brussels 2024. It's a proof of concept (PoC) and its future maintenance is uncertain. For questions or support, please reach out to [Blossom Labs](https://blossom.software).
22 changes: 22 additions & 0 deletions packages/token-list/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"organizeImports": {
"enabled": false
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentWidth": 2,
"indentStyle": "space"
}
}
21 changes: 21 additions & 0 deletions packages/token-list/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "evmcrispr-token-list",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/main.ts",
"scripts": {
"build": "tsc src/main.ts && rollup -c",
"create-func": "fleek functions create --name",
"deploy-func": "fleek functions deploy --noBundle --path dist/main.js --name",
"lint": "biome check --write && biome format --write && biome lint --write"
},
"devDependencies": {
"@biomejs/biome": "^1.8.3",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-typescript": "^11.1.6",
"rollup": "^2.60.0",
"typescript": "^5.2.2"
},
"license": "MIT"
}
16 changes: 16 additions & 0 deletions packages/token-list/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { nodeResolve } from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";

export default {
input: "src/main.ts",
output: {
dir: "dist",
format: "es",
inlineDynamicImports: true,
banner: 'import { Buffer } from "node:buffer";',
},
plugins: [
nodeResolve(), // Needed to bundle the assets from node_modules
typescript(),
],
};
25 changes: 25 additions & 0 deletions packages/token-list/src/coingecko.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type Network = {
id: string;
chain_identifier: number | null;
name: string;
shortname: string;
native_coin_id: string;
};

export async function getNetworkName(
chainId: number,
): Promise<{ name: string; id: string }> {
const networks: Network[] = await fetch(
"https://api.coingecko.com/api/v3/asset_platforms",
).then((res) => res.json());
const network = networks.find(
(network) => network.chain_identifier === chainId,
);
if (!network) {
throw {
status: 404,
body: "Network not found",
};
}
return { name: network.name, id: network.id };
}
120 changes: 120 additions & 0 deletions packages/token-list/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { getNetworkName } from "./coingecko";

type RequestObject = {
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
headers?: {
[key: string]: string;
} | null;
path: string;
query?: {
[key: string]: string | string[];
} | null;
body?: string | null;
};

type ResponseObject =
| {
status: number;
headers?: {
[key: string]: string;
} | null;
body?: string;
}
| string
| ArrayBuffer;

export async function main(params: RequestObject): Promise<ResponseObject> {
try {
const { chainId } = processParams(params);
const { name: networkName, id: networkId } = await getNetworkName(chainId);
const coingeckoTokenList = `https://tokens.coingecko.com/${networkId}/all.json`;
const superfluidTokenList =
"https://raw.githubusercontent.com/superfluid-finance/tokenlist/main/superfluid.extended.tokenlist.json";
const tokenLists = await Promise.all([
fetch(coingeckoTokenList).then((res) => res.json()),
fetch(superfluidTokenList).then((res) => res.json()),
]);
const lastTimestamp = Math.max(
...tokenLists.map((tokenList) => new Date(tokenList.timestamp).getTime()),
);
const tokenList = {
name: `EVMcrispr Token List (${networkName})`,
logoURI: "https://evmcrispr.com/favicon.ico",
timestamp: new Date(lastTimestamp).toISOString(),
tokens: tokenLists[0].tokens
.concat(
tokenLists[1].tokens.filter((token) => token.chainId === chainId),
)
.reduce((acc, token) => {
if (acc.find((t) => t.address === token.address)) {
return acc;
}
acc.push(token);
return acc;
}, []),
version: {
patch: 1,
},
};
return {
status: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify(tokenList),
};
} catch (error) {
if (isErrorWithStatusAndBody(error)) {
return error;
}
return {
status: 500,
body: "Internal Server Error",
};
}
}

// main({ method: "GET", path: "/v0/1" }).then((res) => {
// console.log(res);
// });

function isErrorWithStatusAndBody(
error: unknown,
): error is { status: number; body: string } {
const err = error as { [key: string]: unknown };
return (
err &&
typeof err === "object" &&
typeof err.status === "number" &&
typeof err.body === "string"
);
}

function processParams(params: RequestObject): { chainId: number } {
const { method, path } = params;

if (method !== "GET") {
throw {
status: 405,
body: "Method Not Allowed",
};
}

if (!path.startsWith("/v0/")) {
throw {
status: 400,
body: "Invalid URL",
};
}
const [, , chainId] = path.split("/");

if (!Number(chainId)) {
throw {
status: 400,
body: "Invalid chainId",
};
}

return { chainId: Number(chainId) };
}

0 comments on commit 2feb331

Please sign in to comment.