-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change default tokenlist to custom one
- Loading branch information
1 parent
ee98955
commit 2feb331
Showing
8 changed files
with
299 additions
and
4 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
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). |
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,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" | ||
} | ||
} |
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,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" | ||
} |
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,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(), | ||
], | ||
}; |
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,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 }; | ||
} |
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,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) }; | ||
} |