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
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
63 changes: 63 additions & 0 deletions scripts/cacheTradablePairs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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(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(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 (!addressesForChain) {
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