diff --git a/.github/workflows/sync-trustwallet-assets.yml b/.github/workflows/sync-trustwallet-assets.yml
new file mode 100644
index 000000000..8f5c70864
--- /dev/null
+++ b/.github/workflows/sync-trustwallet-assets.yml
@@ -0,0 +1,50 @@
+name: Sync
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 0 * * *'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ main:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+
+ - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
+ with:
+ node-version-file: '.nvmrc'
+
+ - run: corepack enable pnpm
+
+ - run: pnpm install --frozen-lockfile
+
+ - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+ with:
+ path: workspace/sync-trustwallet-assets/repo
+ repository: trustwallet/assets
+ ref: master
+
+ - run: pnpm turbo run sync
+ working-directory: workspace/sync-trustwallet-assets
+
+ - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
+ with:
+ commit-message: 'sync(assets): trustwallet/assets'
+ title: 'sync(assets): trustwallet/assets'
+ committer: Frontmatter Bot <${{ github.actor }}@users.noreply.github.com>
+ author: Frontmatter Bot <${{ github.actor }}@users.noreply.github.com>
+ body: |
+ #### What this PR does / why we need it:
+
+ Sync latest changes from `trustwallet/assets` repository.
+
+ branch: sync/trustwallet/assets
diff --git a/.idea/frontmatter.iml b/.idea/frontmatter.iml
index ce189958b..96142281e 100644
--- a/.idea/frontmatter.iml
+++ b/.idea/frontmatter.iml
@@ -5,6 +5,8 @@
+
+
diff --git a/packages/example/package.json b/packages/example/package.json
index 1bc73218a..ce36ff187 100644
--- a/packages/example/package.json
+++ b/packages/example/package.json
@@ -5,7 +5,6 @@
"repository": {
"url": "git+https://github.com/levaintech/frontmatter"
},
- "type": "module",
"main": "./dist/index.js",
"source": "./src/index.ts",
"types": "./dist/index.d.ts",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9bde65982..be0b49277 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -95,6 +95,16 @@ importers:
specifier: ^0.5.6
version: 0.5.6(prettier@3.0.3)
+ workspace/sync-trustwallet-assets:
+ dependencies:
+ yaml:
+ specifier: ^2.3.4
+ version: 2.3.4
+ devDependencies:
+ '@workspace/tsconfig':
+ specifier: workspace:*
+ version: link:../tsconfig
+
workspace/tsconfig: {}
packages:
@@ -4258,6 +4268,11 @@ packages:
engines: {node: '>= 14'}
dev: true
+ /yaml@2.3.4:
+ resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
+ engines: {node: '>= 14'}
+ dev: false
+
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
diff --git a/workspace/eslint-config/index.js b/workspace/eslint-config/index.js
index 926944808..1eba5a72a 100644
--- a/workspace/eslint-config/index.js
+++ b/workspace/eslint-config/index.js
@@ -1,10 +1,14 @@
module.exports = [
+ {
+ ignores: ['**/dist/*'],
+ },
require('@eslint/js').configs.recommended,
{
files: ['**/*.ts', '**/*.tsx'],
plugins: {
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'),
},
+ rules: require('@typescript-eslint/eslint-plugin').configs.recommended.rules,
languageOptions: {
parser: require('@typescript-eslint/parser'),
},
diff --git a/workspace/sync-trustwallet-assets/.gitignore b/workspace/sync-trustwallet-assets/.gitignore
new file mode 100644
index 000000000..55d62c108
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/.gitignore
@@ -0,0 +1 @@
+repo/
\ No newline at end of file
diff --git a/workspace/sync-trustwallet-assets/package.json b/workspace/sync-trustwallet-assets/package.json
new file mode 100644
index 000000000..ea5890fbc
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@workspace/sync-trustwallet-assets",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "build": "tsc --project tsconfig.build.json",
+ "clean": "rm -rf dist",
+ "lint": "eslint .",
+ "sync": "node dist/index.js"
+ },
+ "lint-staged": {
+ "*": [
+ "prettier --write --ignore-unknown"
+ ],
+ "*.{ts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ]
+ },
+ "dependencies": {
+ "yaml": "^2.3.4"
+ },
+ "devDependencies": {
+ "@workspace/tsconfig": "workspace:*"
+ }
+}
diff --git a/workspace/sync-trustwallet-assets/src/gitlog.ts b/workspace/sync-trustwallet-assets/src/gitlog.ts
new file mode 100644
index 000000000..6b4673e8a
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/src/gitlog.ts
@@ -0,0 +1,17 @@
+import { exec } from 'node:child_process';
+
+export function getAuthorName(filepath: string): Promise {
+ return new Promise((resolve, reject) => {
+ exec(`git log -1 --pretty=format:"%ae" ${filepath}`, (error, stdout, stderr) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+ if (stderr) {
+ reject(stderr);
+ return;
+ }
+ resolve(stdout);
+ });
+ });
+}
diff --git a/workspace/sync-trustwallet-assets/src/index.ts b/workspace/sync-trustwallet-assets/src/index.ts
new file mode 100644
index 000000000..f3bc2cc6b
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/src/index.ts
@@ -0,0 +1,12 @@
+import { join } from 'node:path';
+import process from 'node:process';
+
+import { Eip155Sync } from './sync';
+
+export async function sync(): Promise {
+ const cwd = process.cwd();
+
+ await new Eip155Sync(join(cwd, 'repo/blockchains/ethereum/assets'), join(cwd, '../../packages/eip155/1')).sync();
+}
+
+void sync();
diff --git a/workspace/sync-trustwallet-assets/src/sync.ts b/workspace/sync-trustwallet-assets/src/sync.ts
new file mode 100644
index 000000000..6fcc59362
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/src/sync.ts
@@ -0,0 +1,110 @@
+import { copyFile, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
+
+import { stringify } from 'yaml';
+
+import { getAuthorName } from './gitlog';
+
+interface Info {
+ name: string;
+ website: string;
+ description: string;
+ explorer: string;
+ type: string;
+ symbol: string;
+ decimals: number;
+ status: string;
+ id: string;
+ tags: string[];
+ links: {
+ name: string;
+ url: string;
+ }[];
+}
+
+export abstract class Sync {
+ constructor(
+ protected readonly from: string,
+ protected readonly to: string,
+ ) {}
+
+ async sync() {
+ const dirs = await readdir(this.from);
+ // TODO(fuxingloh): Limit to first 100 files for testing
+ for (const dir of dirs.slice(0, 100)) {
+ const info = JSON.parse(
+ await readFile(`${this.from}/${dir}/info.json`, {
+ encoding: 'utf-8',
+ }),
+ ) as Partial;
+
+ if (!(await this.shouldWrite(dir, info))) continue;
+ await this.write(dir, info);
+ }
+ }
+
+ async shouldWrite(dir: string, info: Partial): Promise {
+ // Make sure logo.png and info.json exists
+ const fromLogoStats = await stat(`${this.from}/${dir}/logo.png`);
+ const fromInfoStats = await stat(`${this.from}/${dir}/info.json`);
+ if (!fromLogoStats.isFile() || !fromInfoStats.isFile()) return false;
+
+ // If README.md and logo.png do not exist on the other side, write it
+ const namespace = this.getNamespace(info);
+ const toLogoStats = await stat(`${this.to}/${namespace}/logo.png`);
+ const toReadmeStats = await stat(`${this.to}/${namespace}/README.md`);
+ if (!toLogoStats.isFile() || !toReadmeStats.isFile()) return true;
+
+ // Otherwise, allow overwriting if the author is Frontmatter Bot
+ const name = await getAuthorName(`${this.to}/${namespace}/README.md`);
+ return name === 'Frontmatter Bot';
+ }
+
+ abstract write(dir: string, info: Partial): Promise;
+
+ abstract getNamespace(info: Partial): string;
+
+ createLinks(info: Partial): Info['links'] {
+ const links: Info['links'] = [];
+ if (info.website) links.push({ name: 'website', url: info.website });
+ if (info.explorer) links.push({ name: 'explorer', url: info.explorer });
+
+ if (info.links) {
+ for (const link of info.links) {
+ if (link.name === 'website' || link.name === 'explorer') continue;
+ if (!link.url?.startsWith('https://')) continue;
+ if (!link.name) continue;
+ links.push(link);
+ }
+ }
+ return links;
+ }
+}
+
+export class Eip155Sync extends Sync {
+ async write(dir: string, info: Partial): Promise {
+ const namespace = this.getNamespace(info);
+ await mkdir(`${this.to}/${namespace}`, { recursive: true });
+ await copyFile(`${this.from}/${dir}/logo.png`, `${this.to}/${namespace}/logo.png`);
+ await writeFile(
+ `${this.to}/${namespace}/README.md`,
+ [
+ `---`,
+ stringify({
+ symbol: info.symbol,
+ decimals: info.decimals,
+ tags: info.tags,
+ links: this.createLinks(info),
+ }),
+ `---`,
+ '',
+ `# ${info.name}`,
+ '',
+ info.description,
+ ].join('\n'),
+ );
+ }
+
+ getNamespace(info: Partial): string {
+ return info.type!.toLowerCase() + '/' + info.id;
+ }
+}
diff --git a/workspace/sync-trustwallet-assets/tsconfig.build.json b/workspace/sync-trustwallet-assets/tsconfig.build.json
new file mode 100644
index 000000000..4c94b410f
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/tsconfig.build.json
@@ -0,0 +1,8 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "dist"
+ },
+ "include": ["src"],
+ "exclude": ["**/*.unit.ts"]
+}
diff --git a/workspace/sync-trustwallet-assets/tsconfig.json b/workspace/sync-trustwallet-assets/tsconfig.json
new file mode 100644
index 000000000..1799e7475
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@workspace/tsconfig"
+}
diff --git a/workspace/sync-trustwallet-assets/turbo.json b/workspace/sync-trustwallet-assets/turbo.json
new file mode 100644
index 000000000..55b4485ce
--- /dev/null
+++ b/workspace/sync-trustwallet-assets/turbo.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "https://turborepo.org/schema.json",
+ "extends": ["//"],
+ "pipeline": {
+ "sync": {
+ "dependsOn": ["build"]
+ }
+ }
+}
diff --git a/workspace/tsconfig/tsconfig.json b/workspace/tsconfig/tsconfig.json
index 5922d84c4..f7949d2f9 100644
--- a/workspace/tsconfig/tsconfig.json
+++ b/workspace/tsconfig/tsconfig.json
@@ -4,7 +4,7 @@
"compilerOptions": {
"lib": ["ES2021"],
"target": "ES2020",
- "module": "ES2020",
+ "module": "CommonJS",
"allowJs": true,
"composite": false,
"declaration": true,