Skip to content

Commit 5b0faf3

Browse files
committed
feat: added tokens cache
1 parent bd9558e commit 5b0faf3

File tree

16 files changed

+1124
-24
lines changed

16 files changed

+1124
-24
lines changed

README.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,16 @@ yarn add @mento-protocol/mento-sdk
1616

1717
You can find example usages of the SDK in the [mento-sdk-examples](https://github.com/mento-protocol/mento-sdk-examples) repository. For in-depth documentation and walk through explanations please see the [SDK section](https://docs.mento.org/mento/developers/mento-sdk) of the Mento docs.
1818

19-
## Tradable Pairs Cache
19+
## Tokens & Tradable Pairs Cache
2020

21-
Anytime we launch a new stable token, we need to update the tradable pairs cache.
21+
Anytime we launch a new stable token, we need to update the tokens & tradable pairs caches.
2222

23-
The `yarn cacheTradablePairs` script generates a TypeScript file containing a list of all tradable pairs on the Mento protocol. This file is used to cache the tradable pairs in the SDK and avoid costly re-fetching from the network.
24-
25-
```sh
26-
yarn cacheTradablePairs
27-
```
23+
- The `yarn cacheTokens` script generates a TypeScript file containing a list of all tradable Tokens on the Mento protocol. This cache can be used by UIs to avoid costly async token data lookups.
24+
- The `yarn cacheTradablePairs` script generates a TypeScript file containing a list of all tradable pairs on the Mento protocol. This file is used to cache the tradable pairs in the SDK and avoid costly re-fetching from the network.
2825

2926
## Token Graph Visualization
3027

31-
Current token connectivity on Celo Mainnet (last updated: 2025-09-19):
28+
Current token connectivity on Celo Mainnet (last updated: 2025-10-02):
3229

3330
```mermaid
3431
graph TD
@@ -52,7 +49,6 @@ graph TD
5249
cNGN --- cUSD
5350
cKES --- cUSD
5451
cUSD --- eXOF
55-
5652
```
5753

5854
**Network Stats:** 20 tokens, 19 direct trading pairs

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"test": "jest --runInBand --verbose",
3434
"coverage": "jest --coverage",
3535
"cacheTradablePairs": "ts-node scripts/cacheTradablePairs.ts",
36+
"cacheTokens": "ts-node scripts/cacheTokens/index.ts",
3637
"printTradablePairs": "ts-node scripts/printTradablePairs.ts",
3738
"tradingLimits": "ts-node scripts/printTradingLimits.ts",
3839
"limits": "yarn tradingLimits",

scripts/cacheTokens/README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Token Caching Script
2+
3+
This script fetches token metadata from the blockchain for all unique tokens available on Mento Protocol and caches them in static TypeScript files. It dynamically generates type-safe enums, address mappings, and helper functions.
4+
5+
## Purpose
6+
7+
- **Synchronous Access**: Enables synchronous access to token data without async blockchain calls
8+
- **Type Safety**: Dynamically generates type-safe `TokenSymbol` enum and `TOKEN_ADDRESSES_BY_CHAIN` mapping
9+
- **Performance**: Eliminates network calls for token metadata
10+
- **Offline Support**: Token data available without network connection
11+
- **Zero Hardcoding**: Everything auto-generated from blockchain data
12+
13+
## Usage
14+
15+
### Cache tokens for all supported chains
16+
17+
```bash
18+
yarn cacheTokens
19+
```
20+
21+
### Cache tokens for specific chains
22+
23+
```bash
24+
yarn cacheTokens --chain-ids=42220,44787
25+
```
26+
27+
## Output
28+
29+
The script generates TypeScript files in `src/constants/`:
30+
31+
**Individual token files (per chain):**
32+
33+
- `tokens.42220.ts` - Celo Mainnet tokens (readonly Token[])
34+
- `tokens.44787.ts` - Alfajores Testnet tokens (readonly Token[])
35+
- `tokens.11142220.ts` - Celo Sepolia Testnet tokens (readonly Token[])
36+
37+
**Main index file (dynamically generated):**
38+
39+
- `tokens.ts` - Contains:
40+
- `TokenSymbol` enum - All unique token symbols across all chains
41+
- `TOKEN_ADDRESSES_BY_CHAIN` - Address mapping by chain and symbol
42+
- `getCachedTokens()` - Async token loading function
43+
- `getCachedTokensSync()` - Synchronous token loading function
44+
- `getTokenAddress()` - Helper to get token address by symbol
45+
- `findTokenBySymbol()` - Helper to find token by symbol
46+
47+
## What Gets Generated
48+
49+
Everything is computed dynamically from blockchain data:
50+
51+
**Token enum** - Unique symbols across all chains (e.g., `TokenSymbol.CELO`, `TokenSymbol.cUSD`)
52+
**Address mappings** - Complete `TOKEN_ADDRESSES_BY_CHAIN` for all chains
53+
**Helper functions** - Type-safe address lookups and token search
54+
**Chain support** - Auto-detects supported chains from network config
55+
**Error messages** - Dynamic list of supported chains in error messages
56+
57+
## Configuration
58+
59+
RPC URLs can be configured via environment variables:
60+
61+
- `CELO_RPC_URL` - Celo Mainnet RPC URL (default: <https://forno.celo.org>)
62+
- `ALFAJORES_RPC_URL` - Alfajores RPC URL (default: <https://alfajores-forno.celo-testnet.org>)
63+
- `CELO_SEPOLIA_RPC_URL` - Celo Sepolia RPC URL (default: <https://forno.celo-sepolia.celo-testnet.org>)
64+
65+
## When to Regenerate
66+
67+
Regenerate cached tokens when:
68+
69+
- New tokens are added to the Mento Protocol
70+
- Existing tokens are removed from the Mento Protocol
71+
- Token metadata changes (symbol, name, decimals)
72+
- New chains are added to the protocol
73+
74+
## Example Output
75+
76+
```bash
77+
📡 Cache tokens for chain(s): 42220, 44787, 11142220
78+
🔄 Generating tokens for chain 42220...
79+
✅ Successfully cached 20 tokens to tokens.42220.ts
80+
81+
🔄 Generating tokens for chain 44787...
82+
✅ Successfully cached 20 tokens to tokens.44787.ts
83+
84+
🔄 Generating tokens for chain 11142220...
85+
✅ Successfully cached 20 tokens to tokens.11142220.ts
86+
87+
🔄 Generating tokens.ts index file...
88+
✅ Generated tokens.ts with 23 unique token symbols
89+
```

scripts/cacheTokens/cli.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { SupportedChainId } from '../shared/network'
2+
3+
export interface CommandLineArgs {
4+
targetChainIds?: SupportedChainId[]
5+
}
6+
7+
/**
8+
* Parse command line arguments for token caching script
9+
*/
10+
export function parseCommandLineArgs(): CommandLineArgs {
11+
const args: CommandLineArgs = {}
12+
13+
// Check if specific chain IDs were requested
14+
const chainIdArg = process.argv.find((arg) => arg.startsWith('--chain-ids='))
15+
if (chainIdArg) {
16+
const chainIdsStr = chainIdArg.split('=')[1]
17+
args.targetChainIds = chainIdsStr
18+
.split(',')
19+
.map((id) => parseInt(id.trim(), 10) as SupportedChainId)
20+
}
21+
22+
return args
23+
}
24+
25+
/**
26+
* Print usage tips for the token caching script
27+
*/
28+
export function printUsageTips(): void {
29+
console.log('\n💡 Usage tips:')
30+
console.log(' - To cache tokens for specific chains:')
31+
console.log(' yarn cacheTokens --chain-ids=42220,44787')
32+
console.log(' - To cache tokens for all chains:')
33+
console.log(' yarn cacheTokens')
34+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import { Token } from '../../src/mento'
4+
import { SupportedChainId } from '../shared/network'
5+
6+
/**
7+
* Generate TypeScript file content with token data
8+
*/
9+
export function generateFileContent(
10+
chainId: SupportedChainId,
11+
tokens: Token[]
12+
): string {
13+
const now = new Date().toISOString()
14+
15+
const content = `// This file is auto-generated. Do not edit manually.
16+
// Generated on ${now}
17+
18+
import { Token } from '../mento'
19+
20+
export const tokens${chainId}: readonly Token[] = ${JSON.stringify(tokens, null, 2)} as const
21+
`
22+
23+
return content
24+
}
25+
26+
/**
27+
* Write the generated content to a TypeScript file
28+
*/
29+
export function writeToFile(
30+
chainId: SupportedChainId,
31+
content: string,
32+
scriptDir: string
33+
): string {
34+
// Determine the output directory (src/constants/)
35+
const outputDir = path.resolve(scriptDir, '../../src/constants')
36+
37+
// Ensure the output directory exists
38+
if (!fs.existsSync(outputDir)) {
39+
fs.mkdirSync(outputDir, { recursive: true })
40+
}
41+
42+
// Write to file
43+
const fileName = `tokens.${chainId}.ts`
44+
const filePath = path.join(outputDir, fileName)
45+
fs.writeFileSync(filePath, content)
46+
47+
return fileName
48+
}

scripts/cacheTokens/index.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { providers } from 'ethers'
2+
import { Mento, Token } from '../../src/mento'
3+
import { rpcUrls, SupportedChainId } from '../shared/network'
4+
import { parseCommandLineArgs, printUsageTips } from './cli'
5+
import { generateFileContent, writeToFile } from './fileGenerator'
6+
import { generateTokensIndexFile } from './tokensIndexGenerator'
7+
8+
/**
9+
* Fetch tokens for a specific chain
10+
*/
11+
async function fetchTokensForChain(
12+
chainId: SupportedChainId
13+
): Promise<Token[]> {
14+
const provider = new providers.JsonRpcProvider(rpcUrls[chainId])
15+
const mento = await Mento.create(provider)
16+
17+
console.log(`📡 Fetching tokens from blockchain...`)
18+
const tokens = await mento.getTokensAsync({ cached: false })
19+
console.log(`✅ Fetched ${tokens.length} unique tokens`)
20+
21+
return tokens
22+
}
23+
24+
/**
25+
* Generate and cache tokens for a specific chain
26+
*/
27+
async function generateAndCacheTokens(
28+
chainId: SupportedChainId
29+
): Promise<Token[]> {
30+
const tokens = await fetchTokensForChain(chainId)
31+
32+
// Generate and write the TypeScript file
33+
const content = generateFileContent(chainId, tokens)
34+
const fileName = writeToFile(chainId, content, __dirname)
35+
36+
console.log(
37+
`\n✅ Successfully cached ${tokens.length} tokens to ${fileName}\n`
38+
)
39+
40+
return tokens
41+
}
42+
43+
/**
44+
* Main function that orchestrates the entire caching process
45+
*/
46+
export async function main(): Promise<void> {
47+
const args = parseCommandLineArgs()
48+
49+
// Determine which chain IDs to process
50+
const chainIdsToProcess =
51+
args.targetChainIds ||
52+
(Object.keys(rpcUrls).map(Number) as SupportedChainId[])
53+
54+
console.log(`📡 Cache tokens for chain(s): ${chainIdsToProcess.join(', ')}`)
55+
56+
// Store tokens by chain for index file generation
57+
const tokensByChain: { [chainId: number]: Token[] } = {}
58+
59+
// Generate tokens for specified chain IDs
60+
for (const chainId of chainIdsToProcess) {
61+
console.log(`\n🔄 \x1b[1mGenerating tokens for chain ${chainId}...\x1b[0m`)
62+
try {
63+
const tokens = await generateAndCacheTokens(chainId as SupportedChainId)
64+
tokensByChain[chainId] = tokens
65+
} catch (error) {
66+
console.error(`❌ Error generating tokens for chain ${chainId}:`, error)
67+
}
68+
}
69+
70+
// Generate the main tokens.ts index file with enums and mappings
71+
console.log(`\n🔄 \x1b[1mGenerating tokens.ts index file...\x1b[0m`)
72+
generateTokensIndexFile(tokensByChain, __dirname)
73+
74+
printUsageTips()
75+
}
76+
77+
// Run the script if called directly
78+
if (require.main === module) {
79+
main()
80+
.then(() => process.exit(0))
81+
.catch((error) => {
82+
console.error(error)
83+
process.exit(1)
84+
})
85+
}

0 commit comments

Comments
 (0)