Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@mento-protocol/mento-core-ts": "^0.2.0"
"@mento-protocol/mento-core-ts": "^0.2.0",
"mento-router-ts": "^0.2.0"
},
"peerDependencies": {
"ethers": "^5.7"
Expand Down
66 changes: 66 additions & 0 deletions scripts/cacheTradablePairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const rpcUrls = {
42220: 'https://forno.celo.org',
62320: 'https://baklava-forno.celo-testnet.org',
44787: 'https://alfajores-forno.celo-testnet.org',
}

import { providers } from 'ethers'
import { Mento } from '../src/mento'
import fs from 'fs'
import path from 'path'

async function getTradablePairsForNetwork(chainId: number, rpcUrl: string) {
const provider = new providers.JsonRpcProvider(rpcUrl)
const mento = await Mento.create(provider)
return await mento.getTradablePairs(true)
}

async function main() {
const results: Record<number, any> = {}

// Get pairs for each network
for (const [chainId, rpcUrl] of Object.entries(rpcUrls)) {
console.log(`Fetching pairs for chain ${chainId}...`)
try {
results[Number(chainId)] = await getTradablePairsForNetwork(
Number(chainId),
rpcUrl
)
} catch (e) {
console.error(`Error fetching pairs for chain ${chainId}:`, e)
}
}

// Generate TypeScript file content
const fileContent = `// THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.
import { TradablePair } from '../mento'

export const TRADABLE_PAIRS: Record<number, TradablePair[]> = ${JSON.stringify(
results,
null,
2
)};

export function getCachedTradablePairs(chainId: number): TradablePair[] | undefined {
return TRADABLE_PAIRS[chainId]
}
`

// Ensure constants directory exists
const constantsDir = path.join(__dirname, '../src/constants')
if (!fs.existsSync(constantsDir)) {
fs.mkdirSync(constantsDir, { recursive: true })
}

// Write the file
const filePath = path.join(constantsDir, 'tradablePairs.ts')
fs.writeFileSync(filePath, fileContent)
console.log(`Generated ${filePath}`)
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
66 changes: 66 additions & 0 deletions scripts/printTradablePairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { providers } from 'ethers'
import { Mento } from '../src/mento'

const rpcUrls: Record<number, string> = {
42220: 'https://forno.celo.org',
62320: 'https://baklava-forno.celo-testnet.org',
44787: 'https://alfajores-forno.celo-testnet.org',
}

async function main() {
// Expect the chain id as the first command-line argument.
const args = process.argv.slice(2)
if (args.length < 1) {
console.error('Usage: ts-node printTradablePairs.ts <chainId>')
process.exit(1)
}

const chainId = Number(args[0])
if (!rpcUrls[chainId]) {
console.error(
`Chain id ${chainId} not supported. Supported chain ids: ${Object.keys(
rpcUrls
).join(', ')}`
)
process.exit(1)
}

const rpcUrl = rpcUrls[chainId]
const provider = new providers.JsonRpcProvider(rpcUrl)

// Create a Mento instance using the provider
const mento = await Mento.create(provider)

// Optional: verify that the provider's network matches the requested chain id.
const network = await provider.getNetwork()
if (network.chainId !== chainId) {
console.warn(
`Warning: provider network chain id (${network.chainId}) does not match requested chain id (${chainId})`
)
}

// Fetch tradable pairs. Here, we pass "true" to use the cached pairs as defined in getTradablePairs.
const pairs = await mento.getTradablePairs(true)

console.log(`Tradable pairs for chain ${chainId}:\n`)
for (const pair of pairs) {
const [asset1, asset2] = pair.assets
console.log(`${pair.id}:`)
console.log(` Assets:`)
console.log(` ${asset1.symbol}: ${asset1.address}`)
console.log(` ${asset2.symbol}: ${asset2.address}`)
console.log(` Exchange Path:`)
for (const hop of pair.path) {
console.log(` Provider: ${hop.providerAddr}`)
console.log(` Exchange ID: ${hop.id}`)
console.log(` Assets: ${hop.assets[0]} -> ${hop.assets[1]}`)
console.log()
}
console.log('---')
}
}

main().catch((error) => {
console.error('Error:', error)
process.exit(1)
})
121 changes: 121 additions & 0 deletions scripts/swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { BigNumber, Contract, providers, utils, Wallet } from 'ethers'
import { Mento } from '../src/mento'

// Start Generation Here
const rpcUrls: Record<number, string> = {
42220: 'https://forno.celo.org',
62320: 'https://baklava-forno.celo-testnet.org',
44787: 'https://alfajores-forno.celo-testnet.org',
}

async function main() {
// Read command-line arguments
const args = process.argv.slice(2)
if (args.length < 4) {
console.error(
'Usage: ts-node swap.ts <chainId> <pairId> <direction> <amount>'
)
process.exit(1)
}

const [chainIdStr, pairId, direction, amountStr] = args
const chainId = Number(chainIdStr)

if (!rpcUrls[chainId]) {
console.error(
`Chain id ${chainId} not supported. Supported chain ids: ${Object.keys(
rpcUrls
).join(', ')}`
)
process.exit(1)
}

// Read private key from environment
const privateKey = process.env.PRIVATE_KEY
if (!privateKey) {
console.error('Error: PRIVATE_KEY environment variable not set.')
process.exit(1)
}

// Create provider and signer
const provider = new providers.JsonRpcProvider(rpcUrls[chainId])
const wallet = new Wallet(privateKey, provider)

// Create Mento instance
const mento = await Mento.create(wallet)

// Fetch tradable pairs
const pairs = await mento.getTradablePairsWithPath(true)

// Find the specified tradable pair
const tradablePair = pairs.find((p) => p.id === pairId)
if (!tradablePair) {
console.error(`Tradable pair ${pairId} not found.`)
process.exit(1)
}

// Determine tokenIn and tokenOut based on direction
let tokenIn = tradablePair.assets[0].address
let tokenOut = tradablePair.assets[1].address
if (direction.toLowerCase() === 'reverse') {
;[tokenIn, tokenOut] = [tokenOut, tokenIn]
}
// Get token decimals
const tokenInContract = new Contract(
tokenIn,
[
{
name: 'decimals',
type: 'function',
inputs: [],
outputs: [{ type: 'uint8' }],
stateMutability: 'view',
},
],
provider
)
const decimals = await tokenInContract.decimals()

// Parse amount and scale by decimals
const amountIn = utils.parseUnits(amountStr, decimals)

// Get amountOut from Mento
const amountOut = await mento.getAmountOut(
tokenIn,
tokenOut,
amountIn,
)

// Calculate minAmountOut with 5% slippage
const minAmountOut = amountOut.mul(95).div(100)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: would be nice to make this an optional param


// Increase trading allowance
console.log('Increasing trading allowance...')
const allowanceTxReq = await mento.increaseTradingAllowance(
tokenIn,
amountIn,
tradablePair
)
const allowanceTx = await wallet.sendTransaction(allowanceTxReq)
console.log(`Allowance transaction sent: ${allowanceTx.hash}`)
await allowanceTx.wait()
console.log('Allowance transaction confirmed.')

// Perform swap
console.log('Performing swap...')
const swapTxReq = await mento.swapIn(
tokenIn,
tokenOut,
amountIn,
minAmountOut,
)
const swapTx = await wallet.sendTransaction(swapTxReq)
console.log(`Swap transaction sent: ${swapTx.hash}`)
await swapTx.wait()
console.log('Swap transaction confirmed.')
}

main().catch((error) => {
console.error('Error:', error)
process.exit(1)
})
21 changes: 21 additions & 0 deletions src/constants/addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const addresses: ContractAddressMap = {
MentoToken: '0x7FF62f59e3e89EA34163EA1458EEBCc81177Cfb6',
TimelockController: '0x890DB8A597940165901372Dd7DB61C9f246e2147',
Locking: '0x001Bb66636dCd149A1A2bA8C50E408BdDd80279C',
MentoRouter: '0xbe729350f8cdfc19db6866e8579841188ee57f67',
Broker: '0x777A8255cA72412f0d706dc03C9D1987306B4CaD',
BiPoolManager: '0x22d9db95E6Ae61c104A7B6F6C78D7993B94ec901',
BreakerBox: '0x303ED1df62Fa067659B586EbEe8De0EcE824Ab39',
Expand All @@ -32,6 +33,7 @@ export const addresses: ContractAddressMap = {
MentoToken: '0x3eDd2f7c90e2E931c817a44302Af7112E84be6Cc',
TimelockController: '0xa0Ad8DD40104556122c21dF50eD14bb1B53A3338',
Locking: '0x537CaE97C588C6DA64A385817F3D3563DDCf0591',
MentoRouter: '0xe6101a457a69b53e298e35a7f6e3dcb0390df02a',
Broker: '0xD3Dff18E465bCa6241A244144765b4421Ac14D09',
BiPoolManager: '0x9B64E8EaBD1a035b148cE970d3319c5C3Ad53EC3',
BreakerBox: '0xC76BDf0AFb654888728003683cf748A8B1b4f5fD',
Expand All @@ -54,6 +56,7 @@ export const addresses: ContractAddressMap = {
MentoToken: '0x8942330eCB5A6c808aac3Aec3C6aab6D8CF436FE',
TimelockController: '0x8c045769087F9de69B70949ED7fC23c14Db71e20',
Locking: '0x1E15b108c51a0cAEAFf1a0E6f27A853Bde1AA2e6',
MentoRouter: '0xC5449dbB0aF89F5E3C8E0e1611966E1964F891b1',
Broker: '0x6723749339e320E1EFcd9f1B0D997ecb45587208',
BiPoolManager: '0xFF9a3da00F42839CD6D33AD7adf50bCc97B41411',
BreakerBox: '0x5Ea5A5F694F10de979BEeC7b8041E9f931F54bc7',
Expand All @@ -69,3 +72,21 @@ export const addresses: ContractAddressMap = {
SortedOracles: '0x88A187a876290E9843175027902B9f7f1B092c88',
},
}

export type Identifier = keyof ContractAddressMap[keyof ContractAddressMap]

export function getAddress(identifier: Identifier, chainId: number): string {
const addressesForChain = addresses[chainId]
if (!addresses) {
throw new Error(`No addresses found for chain ID ${chainId}`)
}

const address = addressesForChain[identifier]
if (!address) {
throw new Error(
`Address not found for identifier ${identifier} on chain ID ${chainId}`
)
}

return address
}
Loading