Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] test rest api #3146

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f131926
fetch best rfq quote with query params
bigboydiamonds Sep 13, 2024
acd653c
fetch best quote for sdk and rfq api
bigboydiamonds Sep 13, 2024
6ada55a
return largest quoted origin amount on 1m size with respective bridge…
bigboydiamonds Sep 13, 2024
8530122
update endpoint naming, errors
bigboydiamonds Sep 13, 2024
4428e8d
Merge branch 'master' into api/bridge-max-amounts
bigboydiamonds Sep 16, 2024
69f36c3
implement mvc pattern
bigboydiamonds Sep 16, 2024
b48a36e
return max/min origin amounts
bigboydiamonds Sep 17, 2024
d4fac70
clean
bigboydiamonds Sep 17, 2024
5e5e2b7
...
bigboydiamonds Sep 17, 2024
7eab9a7
revert package.json changes
bigboydiamonds Sep 17, 2024
f56517a
pass in from/to token addresses in query params
bigboydiamonds Sep 17, 2024
ddb245d
tests
bigboydiamonds Sep 17, 2024
139e27c
fix tests
bigboydiamonds Sep 17, 2024
95a93ca
fix test name
bigboydiamonds Sep 17, 2024
96ec975
return parsed values
bigboydiamonds Sep 17, 2024
be863d5
account for eth addresses
bigboydiamonds Sep 17, 2024
c6cd501
clean
bigboydiamonds Sep 17, 2024
bb9aa82
refactor: clean getBridgeLimitsController
bigboydiamonds Sep 18, 2024
9fb48d8
query lower limit range
bigboydiamonds Sep 18, 2024
8241170
example query
bigboydiamonds Sep 18, 2024
eb2f391
add error
bigboydiamonds Sep 18, 2024
94846ac
return error if route does not exist
bigboydiamonds Sep 18, 2024
bbd8ef5
add lower limit values
bigboydiamonds Sep 18, 2024
baea359
update lower limit values
bigboydiamonds Sep 18, 2024
55c8d85
use full range for checking min amount
bigboydiamonds Sep 18, 2024
aa521c3
hardcode max origin value
bigboydiamonds Sep 18, 2024
01f89bd
Merge branch 'api/bridge-max-amounts' into test-api
bigboydiamonds Sep 18, 2024
cd5f57e
update
bigboydiamonds Sep 18, 2024
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
2 changes: 2 additions & 0 deletions packages/rest-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
"postinstall": "npm run build"
},
"dependencies": {
"@ethersproject/address": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@ethersproject/units": "5.7.0",
"@synapsecns/sdk-router": "^0.11.1",
"bignumber": "^1.1.0",
"cors": "^2.8.5",
"ethers": "5.7.2",
"express": "^4.18.2",
"express-validator": "^7.2.0",
Expand Down
23 changes: 23 additions & 0 deletions packages/rest-api/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
// import express from 'express'

// import routes from './routes'

// const app = express()
// const port = process.env.PORT || 3000

// app.use(express.json())
// app.use('/', routes)

// export const server = app.listen(port, () => {
// console.log(`Server listening at ${port}`)
// })

import express from 'express'
import cors from 'cors'

import routes from './routes'

const app = express()
const port = process.env.PORT || 3000

// Configure CORS to accept requests from localhost:3001
const corsOptions = {
origin: 'http://localhost:3001',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true, // If you need to support cookies or authentication headers
}

app.use(cors(corsOptions)) // Apply the CORS middleware
app.use(express.json())
app.use('/', routes)

Expand Down
3 changes: 1 addition & 2 deletions packages/rest-api/src/constants/bridgeable.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ZeroAddress } from './index'
import { BridgeableToken } from '../types'
import { CHAINS } from './chains'

const ZeroAddress = '0x0000000000000000000000000000000000000000'

export const GOHM: BridgeableToken = {
addresses: {
[CHAINS.ETHEREUM.id]: '0x0ab87046fBb341D058F17CBC4c1133F25a20a52f',
Expand Down
2 changes: 2 additions & 0 deletions packages/rest-api/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const VALID_BRIDGE_MODULES = [
'SynapseCCTP',
'SynapseRFQ',
]

export const ZeroAddress = '0x0000000000000000000000000000000000000000'
108 changes: 108 additions & 0 deletions packages/rest-api/src/controllers/getBridgeLimitsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { validationResult } from 'express-validator'
import { BigNumber } from 'ethers'
import { parseUnits } from '@ethersproject/units'

import { Synapse } from '../services/synapseService'
import { tokenAddressToToken } from '../utils/tokenAddressToToken'
import { formatBNToString } from '../utils/formatBNToString'

export const getBridgeLimitsController = async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
try {
const { fromChain, fromToken, toChain, toToken } = req.query

const fromTokenInfo = tokenAddressToToken(fromChain, fromToken)
const toTokenInfo = tokenAddressToToken(toChain, toToken)

const upperLimitValue = parseUnits('1000000', fromTokenInfo.decimals)
const upperLimitBridgeQuotes = await Synapse.allBridgeQuotes(
Number(fromChain),
Number(toChain),
fromTokenInfo.address,
toTokenInfo.address,
upperLimitValue
)

const lowerLimitValues = ['0.01', '10']
let lowerLimitBridgeQuotes = null

for (const limit of lowerLimitValues) {
const lowerLimitAmount = parseUnits(limit, fromTokenInfo.decimals)

lowerLimitBridgeQuotes = await Synapse.allBridgeQuotes(
Number(fromChain),
Number(toChain),
fromTokenInfo.address,
toTokenInfo.address,
lowerLimitAmount
)

if (lowerLimitBridgeQuotes && lowerLimitBridgeQuotes.length > 0) {
break
}
}

const maxBridgeAmountQuote = upperLimitBridgeQuotes.reduce(
(maxQuote, currentQuote) => {
const currentMaxAmount = currentQuote.maxAmountOut
const maxAmount = maxQuote ? maxQuote.maxAmountOut : BigNumber.from(0)

return currentMaxAmount.gt(maxAmount) ? currentQuote : maxQuote
},
null
)

const minBridgeAmountQuote = lowerLimitBridgeQuotes.reduce(
(minQuote, currentQuote) => {
const currentFeeAmount = currentQuote.feeAmount
const minFeeAmount = minQuote ? minQuote.feeAmount : null

return !minFeeAmount || currentFeeAmount.lt(minFeeAmount)
? currentQuote
: minQuote
},
null
)

if (!maxBridgeAmountQuote || !minBridgeAmountQuote) {
return res.json({
maxOriginAmount: null,
minOriginAmount: null,
})
}

const maxAmountOriginQueryTokenOutInfo = tokenAddressToToken(
toChain,
maxBridgeAmountQuote.destQuery.tokenOut
)

const minAmountOriginQueryTokenOutInfo = tokenAddressToToken(
fromChain,
minBridgeAmountQuote.originQuery.tokenOut
)

const maxOriginAmount = formatBNToString(
maxBridgeAmountQuote.maxAmountOut,
maxAmountOriginQueryTokenOutInfo.decimals
)

const minOriginAmount = formatBNToString(
minBridgeAmountQuote.feeAmount,
minAmountOriginQueryTokenOutInfo.decimals
)

return res.json({
maxOriginAmount,
minOriginAmount,
})
} catch (err) {
res.status(500).json({
error:
'An unexpected error occurred in /getBridgeLimits. Please try again later.',
details: err.message,
})
}
}
41 changes: 41 additions & 0 deletions packages/rest-api/src/routes/getBridgeLimitsRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import express from 'express'
import { check } from 'express-validator'
import { isAddress } from '@ethersproject/address'

import { CHAINS_ARRAY } from '../constants/chains'
import { showFirstValidationError } from '../middleware/showFirstValidationError'
import { getBridgeLimitsController } from '../controllers/getBridgeLimitsController'

const router = express.Router()

router.get(
'/',
[
check('fromChain')
.isNumeric()
.custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value)))
.withMessage('Unsupported fromChain')
.exists()
.withMessage('originChainId is required'),
check('toChain')
.isNumeric()
.custom((value) => CHAINS_ARRAY.some((c) => c.id === Number(value)))
.withMessage('Unsupported toChain')
.exists()
.withMessage('toChain is required'),
check('fromToken')
.exists()
.withMessage('fromToken is required')
.custom((value) => isAddress(value))
.withMessage('Invalid fromToken address'),
check('toToken')
.exists()
.withMessage('toToken is required')
.custom((value) => isAddress(value))
.withMessage('Invalid toToken address'),
],
showFirstValidationError,
getBridgeLimitsController
)

export default router
2 changes: 2 additions & 0 deletions packages/rest-api/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import swapRoute from './swapRoute'
import swapTxInfoRoute from './swapTxInfoRoute'
import bridgeRoute from './bridgeRoute'
import bridgeTxInfoRoute from './bridgeTxInfoRoute'
import getBridgeLimitsRoute from './getBridgeLimitsRoute'
import getSynapseTxIdRoute from './getSynapseTxIdRoute'
import getBridgeTxStatusRoute from './getBridgeTxStatusRoute'
import getDestinationTxRoute from './getDestinationTxRoute'
Expand All @@ -17,6 +18,7 @@ router.use('/swap', swapRoute)
router.use('/swapTxInfo', swapTxInfoRoute)
router.use('/bridge', bridgeRoute)
router.use('/bridgeTxInfo', bridgeTxInfoRoute)
router.use('/getBridgeLimits', getBridgeLimitsRoute)
router.use('/getSynapseTxId', getSynapseTxIdRoute)
router.use('/getBridgeTxStatus', getBridgeTxStatusRoute)
router.use('/getDestinationTx', getDestinationTxRoute)
Expand Down
81 changes: 81 additions & 0 deletions packages/rest-api/src/tests/getBridgeLimits.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import request from 'supertest'
import express from 'express'

import getBridgeLimitsRoute from '../routes/getBridgeLimitsRoute'

const app = express()
app.use('/getBridgeLimits', getBridgeLimitsRoute)

describe('Get Bridge Limits Route', () => {
it('should return min/max origin amounts for valid input', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: 1,
fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
toChain: 10,
toToken: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
})

expect(response.status).toBe(200)
expect(response.body).toHaveProperty('maxOriginAmount')
expect(response.body).toHaveProperty('minOriginAmount')
}, 10_000)

it('should return 400 for unsupported fromChain', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: '999',
toChain: '137',
fromToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff',
toToken: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
})
expect(response.status).toBe(400)
expect(response.body.error).toHaveProperty(
'message',
'Unsupported fromChain'
)
}, 10_000)

it('should return 400 for unsupported ', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: '999',
toChain: '137',
fromToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff',
toToken: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
})
expect(response.status).toBe(400)
expect(response.body.error).toHaveProperty(
'message',
'Unsupported fromChain'
)
}, 10_000)

it('should return 400 for unsupported toChain', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: '137',
toChain: '999',
fromToken: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
toToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff',
})
expect(response.status).toBe(400)
expect(response.body.error).toHaveProperty('message', 'Unsupported toChain')
}, 10_000)

it('should return 400 for missing fromToken', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: '1',
toChain: '137',
toToken: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff',
})
expect(response.status).toBe(400)
expect(response.body.error).toHaveProperty('field', 'fromToken')
}, 10_000)

it('should return 400 for missing toToken', async () => {
const response = await request(app).get('/getBridgeLimits').query({
fromChain: '1',
toChain: '137',
fromToken: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
})
expect(response.status).toBe(400)
expect(response.body.error).toHaveProperty('field', 'toToken')
}, 10_000)
})
26 changes: 26 additions & 0 deletions packages/rest-api/src/utils/tokenAddressToToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getAddress } from '@ethersproject/address'

import { BRIDGE_MAP } from '../constants/bridgeMap'
import { ZeroAddress } from '../constants'

export const tokenAddressToToken = (chain: string, tokenAddress: string) => {
let address
if (tokenAddress === ZeroAddress) {
address = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
} else {
address = getAddress(tokenAddress)
}
const chainData = BRIDGE_MAP[chain]
if (!chainData) {
return null
}
const tokenInfo = chainData[address]
if (!tokenInfo) {
return null
}
return {
address,
symbol: tokenInfo.symbol,
decimals: tokenInfo.decimals,
}
}
42 changes: 42 additions & 0 deletions packages/synapse-interface/pages/state-managed-bridge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,42 @@ import { resetBridgeQuote } from '@/slices/bridgeQuote/reducer'
import { fetchBridgeQuote } from '@/slices/bridgeQuote/thunks'
import { useIsBridgeApproved } from '@/utils/hooks/useIsBridgeApproved'

import axios from 'axios'

const fetchBridgeLimits = async (
fromChainId,
toChainId,
fromToken,
toToken
) => {
// Ensure that token addresses exist for the respective chains
if (!fromToken?.addresses[fromChainId] || !toToken?.addresses[toChainId]) {
console.error('Token addresses are not defined for the selected chains.')
return
}

// Construct the URL with parameters
const endpoint = `http://localhost:3000/getBridgeLimits`
const url = `${endpoint}?fromChain=${fromChainId}&fromToken=${fromToken.addresses[fromChainId]}&toChain=${toChainId}&toToken=${toToken.addresses[toChainId]}`

try {
// Make the GET request
const response = await axios.get(url)

// Handle the response data
if (response.status === 200) {
console.log('Bridge limits data:', response.data)
return response.data
} else {
console.error('Failed to fetch bridge limits:', response.statusText)
return null
}
} catch (error) {
console.error('Error fetching bridge limits:', error)
return null
}
}

const StateManagedBridge = () => {
const dispatch = useAppDispatch()
const { address } = useAccount()
Expand All @@ -92,6 +128,12 @@ const StateManagedBridge = () => {
destinationAddress,
}: BridgeState = useBridgeState()

useEffect(() => {
if (fromChainId && toChainId && fromToken && toToken) {
fetchBridgeLimits(fromChainId, toChainId, fromToken, toToken)
}
}, [fromChainId, toChainId, fromToken, toToken])

const { bridgeQuote, isLoading } = useBridgeQuoteState()

const isApproved = useIsBridgeApproved()
Expand Down
Loading
Loading