Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@ yarn add @mento-protocol/mento-sdk

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.

## Tradable Pairs Cache
## Tokens & Tradable Pairs Cache

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

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.

```sh
yarn cacheTradablePairs
```
- 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.
- 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.

## Token Graph Visualization

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

```mermaid
graph TD
Expand All @@ -52,7 +49,6 @@ graph TD
cNGN --- cUSD
cKES --- cUSD
cUSD --- eXOF

```

**Network Stats:** 20 tokens, 19 direct trading pairs
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@mento-protocol/mento-sdk",
"description": "Official SDK for interacting with the Mento Protocol",
"version": "1.12.0",
"version": "1.15.4",
"license": "MIT",
"author": "Mento Labs",
"keywords": [
Expand Down Expand Up @@ -33,6 +33,7 @@
"test": "jest --runInBand --verbose",
"coverage": "jest --coverage",
"cacheTradablePairs": "ts-node scripts/cacheTradablePairs.ts",
"cacheTokens": "ts-node scripts/cacheTokens/index.ts",
"printTradablePairs": "ts-node scripts/printTradablePairs.ts",
"tradingLimits": "ts-node scripts/printTradingLimits.ts",
"limits": "yarn tradingLimits",
Expand Down
89 changes: 89 additions & 0 deletions scripts/cacheTokens/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Token Caching Script

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 a type-safe TokenSymbol enum, address mappings, and helper functions.

## Purpose

- **Synchronous Access**: Enables synchronous access to token data without async blockchain calls
- **Type Safety**: Dynamically generates type-safe `TokenSymbol` enum and `TOKEN_ADDRESSES_BY_CHAIN` mapping
- **Performance**: Eliminates network calls for token metadata
- **Offline Support**: Token data available without network connection
- **Zero Hardcoding**: Everything auto-generated from blockchain data

## Usage

### Cache tokens for all supported chains

```bash
yarn cacheTokens
```

### Cache tokens for specific chains

```bash
yarn cacheTokens --chain-ids=42220,44787
```

## Output

The script generates TypeScript files in `src/constants/`:

**Individual token files (per chain):**

- `tokens.42220.ts` - Celo Mainnet tokens (readonly Token[])
- `tokens.44787.ts` - Alfajores Testnet tokens (readonly Token[])
- `tokens.11142220.ts` - Celo Sepolia Testnet tokens (readonly Token[])

**Main index file (dynamically generated):**

- `tokens.ts` - Contains:
- `TokenSymbol` enum - All unique token symbols across all chains
- `TOKEN_ADDRESSES_BY_CHAIN` - Address mapping by chain and symbol
- `getCachedTokens()` - Async token loading function
- `getCachedTokensSync()` - Synchronous token loading function
- `getTokenAddress()` - Helper to get token address by symbol
- `findTokenBySymbol()` - Helper to find token by symbol

## What Gets Generated

Everything is computed dynamically from blockchain data:

✅ **Token enum** - Unique symbols across all chains (e.g., `TokenSymbol.CELO`, `TokenSymbol.cUSD`)
✅ **Address mappings** - Complete `TOKEN_ADDRESSES_BY_CHAIN` for all chains
✅ **Helper functions** - Type-safe address lookups and token search
✅ **Chain support** - Auto-detects supported chains from network config
✅ **Error messages** - Dynamic list of supported chains in error messages

## Configuration

RPC URLs can be configured via environment variables:

- `CELO_RPC_URL` - Celo Mainnet RPC URL (default: <https://forno.celo.org>)
- `ALFAJORES_RPC_URL` - Alfajores RPC URL (default: <https://alfajores-forno.celo-testnet.org>)
- `CELO_SEPOLIA_RPC_URL` - Celo Sepolia RPC URL (default: <https://forno.celo-sepolia.celo-testnet.org>)

## When to Regenerate

Regenerate cached tokens when:

- New tokens are added to the Mento Protocol
- Existing tokens are removed from the Mento Protocol
- Token metadata changes (symbol, name, decimals)
- New chains are added to the protocol

## Example Output

```bash
📡 Cache tokens for chain(s): 42220, 44787, 11142220
🔄 Generating tokens for chain 42220...
✅ Successfully cached 20 tokens to tokens.42220.ts

🔄 Generating tokens for chain 44787...
✅ Successfully cached 20 tokens to tokens.44787.ts

🔄 Generating tokens for chain 11142220...
✅ Successfully cached 20 tokens to tokens.11142220.ts

🔄 Generating tokens.ts index file...
✅ Generated tokens.ts with 23 unique token symbols
```
34 changes: 34 additions & 0 deletions scripts/cacheTokens/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SupportedChainId } from '../shared/network'

export interface CommandLineArgs {
targetChainIds?: SupportedChainId[]
}

/**
* Parse command line arguments for token caching script
*/
export function parseCommandLineArgs(): CommandLineArgs {
const args: CommandLineArgs = {}

// Check if specific chain IDs were requested
const chainIdArg = process.argv.find((arg) => arg.startsWith('--chain-ids='))
if (chainIdArg) {
const chainIdsStr = chainIdArg.split('=')[1]
args.targetChainIds = chainIdsStr
.split(',')
.map((id) => parseInt(id.trim(), 10) as SupportedChainId)
}

return args
}

/**
* Print usage tips for the token caching script
*/
export function printUsageTips(): void {
console.log('\n💡 Usage tips:')
console.log(' - To cache tokens for specific chains:')
console.log(' yarn cacheTokens --chain-ids=42220,44787')
console.log(' - To cache tokens for all chains:')
console.log(' yarn cacheTokens')
}
65 changes: 65 additions & 0 deletions scripts/cacheTokens/fileGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as fs from 'fs'
import * as path from 'path'
import { Token } from '../../src/mento'
import { SupportedChainId } from '../shared/network'

/**
* Generate TypeScript file content with token data
*/
export function generateFileContent(
chainId: SupportedChainId,
tokens: Token[]
): string {
const now = new Date().toISOString()

// Generate token entries with TokenSymbol enum references
const tokenEntries = tokens
.map((token) => {
// Create a safe enum key (replace special characters)
const safeKey = token.symbol.replace(/[^a-zA-Z0-9_]/g, '_')

return ` {
address: '${token.address}',
symbol: TokenSymbol.${safeKey},
name: '${token.name}',
decimals: ${token.decimals},
}`
})
.join(',\n')

const content = `// This file is auto-generated. Do not edit manually.
// Generated on ${now}

import { Token, TokenSymbol } from '../mento'

export const tokens${chainId}: readonly Token[] = [
${tokenEntries},
] as const
`

return content
}

/**
* Write the generated content to a TypeScript file
*/
export function writeToFile(
chainId: SupportedChainId,
content: string,
scriptDir: string
): string {
// Determine the output directory (src/constants/)
const outputDir = path.resolve(scriptDir, `${process.cwd()}/src/constants`)

// Ensure the output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true })
}

// Write to file
const fileName = `tokens.${chainId}.ts`
const filePath = path.join(outputDir, fileName)
fs.writeFileSync(filePath, content)

return fileName
}
85 changes: 85 additions & 0 deletions scripts/cacheTokens/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { providers } from 'ethers'
import { Mento, Token } from '../../src/mento'
import { rpcUrls, SupportedChainId } from '../shared/network'
import { parseCommandLineArgs, printUsageTips } from './cli'
import { generateFileContent, writeToFile } from './fileGenerator'
import { generateTokensIndexFile } from './tokensIndexGenerator'

/**
* Fetch tokens for a specific chain
*/
async function fetchTokensForChain(
chainId: SupportedChainId
): Promise<Token[]> {
const provider = new providers.JsonRpcProvider(rpcUrls[chainId])
const mento = await Mento.create(provider)

console.log(`📡 Fetching tokens from blockchain...`)
const tokens = await mento.getTokensAsync({ cached: false })
console.log(`✅ Fetched ${tokens.length} unique tokens`)

return tokens
}

/**
* Generate and cache tokens for a specific chain
*/
async function generateAndCacheTokens(
chainId: SupportedChainId
): Promise<Token[]> {
const tokens = await fetchTokensForChain(chainId)

// Generate and write the TypeScript file
const content = generateFileContent(chainId, tokens)
const fileName = writeToFile(chainId, content, __dirname)

console.log(
`\n✅ Successfully cached ${tokens.length} tokens to ${fileName}\n`
)

return tokens
}

/**
* Main function that orchestrates the entire caching process
*/
export async function main(): Promise<void> {
const args = parseCommandLineArgs()

// Determine which chain IDs to process
const chainIdsToProcess =
args.targetChainIds ||
(Object.keys(rpcUrls).map(Number) as SupportedChainId[])

console.log(`📡 Cache tokens for chain(s): ${chainIdsToProcess.join(', ')}`)

// Store tokens by chain for index file generation
const tokensByChain: { [chainId: number]: Token[] } = {}

// Generate tokens for specified chain IDs
for (const chainId of chainIdsToProcess) {
console.log(`\n🔄 \x1b[1mGenerating tokens for chain ${chainId}...\x1b[0m`)
try {
const tokens = await generateAndCacheTokens(chainId as SupportedChainId)
tokensByChain[chainId] = tokens
} catch (error) {
console.error(`❌ Error generating tokens for chain ${chainId}:`, error)
}
}

// Generate the main tokens.ts index file with enums and mappings
console.log(`\n🔄 \x1b[1mGenerating tokens.ts index file...\x1b[0m`)
generateTokensIndexFile(tokensByChain, __dirname)

printUsageTips()
}

// Run the script if called directly
if (require.main === module) {
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
}
Loading