Skip to content

Commit

Permalink
Sdk: bridge module naming (#1531)
Browse files Browse the repository at this point in the history
* Store bridge module name

* Add sanity checks

* Add bridge module name to the returned BridgeQuote

* Check returned bridge module name

* Use bridge module name in `getEstimatedTime`

* Rework coverage for getEstimatedTime

* Define existing bridge/cctp events

* Add `getBridgeModuleName`

* Coverage for `getBridgeModuleName`

* Simplify tests a bit

* Chore: contract/explorer event names in tests
  • Loading branch information
ChiTimesChi authored Nov 2, 2023
1 parent 060df78 commit 9b87daa
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 65 deletions.
34 changes: 27 additions & 7 deletions packages/sdk-router/src/operations/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,27 +118,47 @@ export async function bridgeQuote(
}

/**
* Returns the estimated time for a bridge operation from a given origin chain using a given router.
* Returns the name of the bridge module that emits the given event.
* This will be either SynapseBridge or SynapseCCTP.
*
* @param eventName - The name of the event.
* @returns - The name of the bridge module.
*/
export function getBridgeModuleName(
this: SynapseSDK,
eventName: string
): string {
if (this.synapseRouterSet.allEvents.includes(eventName)) {
return this.synapseRouterSet.bridgeModuleName
}
if (this.synapseCCTPRouterSet.allEvents.includes(eventName)) {
return this.synapseCCTPRouterSet.bridgeModuleName
}
throw new Error('Unknown event')
}

/**
* Returns the estimated time for a bridge operation from a given origin chain using a given bridge module.
* This will be the estimated time for the bridge operation to be completed regardless of the destination chain,
* or the bridge token.
*
* @param originChainId - The ID of the origin chain.
* @param routerAddress - The address of the router deployed on the origin chain.
* @param bridgeNoduleName - The name of the bridge module.
* @returns - The estimated time for a bridge operation, in seconds.
* @throws - Will throw an error if the router address is unknown for the given chain.
* @throws - Will throw an error if the bridge module is unknown for the given chain.
*/
export function getEstimatedTime(
this: SynapseSDK,
originChainId: number,
routerAddress: string
bridgeNoduleName: string
): number {
if (this.synapseRouterSet.getRouter(originChainId, routerAddress)) {
if (this.synapseRouterSet.bridgeModuleName === bridgeNoduleName) {
return this.synapseRouterSet.getEstimatedTime(originChainId)
}
if (this.synapseCCTPRouterSet.getRouter(originChainId, routerAddress)) {
if (this.synapseCCTPRouterSet.bridgeModuleName === bridgeNoduleName) {
return this.synapseCCTPRouterSet.getEstimatedTime(originChainId)
}
throw new Error('Unknown router address')
throw new Error('Unknown bridge module')
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/sdk-router/src/router/routerSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ export type RouterConstructor = new (
*
* The class children should provide the router addresses for each chain, as well as the Router constructor.
*
* @property routerName The name of the router set.
* @property bridgeModuleName The name of the bridge module used by the routers.
* @property routers Collection of Router instances indexed by chainId.
* @property providers Collection of Provider instances indexed by chainId.
*/
export abstract class RouterSet {
abstract readonly routerName: string
abstract readonly bridgeModuleName: string
abstract readonly allEvents: string[]

public routers: {
[chainId: number]: Router
Expand Down Expand Up @@ -206,6 +207,7 @@ export abstract class RouterSet {
originQuery,
destQuery,
estimatedTime,
bridgeModuleName: this.bridgeModuleName,
}
}
}
4 changes: 4 additions & 0 deletions packages/sdk-router/src/router/synapseCCTPRouterSet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ describe('SynapseCCTPRouterSet', () => {
it('Does not create SynapseCCTPRouter instances for chains without providers', () => {
expect(routerSet.routers[SupportedChainId.AVALANCHE]).toBeUndefined()
})

it('Correct bridge module name', () => {
expect(routerSet.bridgeModuleName).toEqual('SynapseCCTP')
})
})

describe('getEstimatedTime', () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/sdk-router/src/router/synapseCCTPRouterSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { CCTP_ROUTER_ADDRESS_MAP, MEDIAN_TIME_CCTP } from '../constants'
* Wrapper class for interacting with a SynapseCCTPRouter contracts deployed on multiple chains.
*/
export class SynapseCCTPRouterSet extends RouterSet {
public readonly routerName = 'SynapseCCTPRouter'
public readonly bridgeModuleName = 'SynapseCCTP'
public readonly allEvents = [
'CircleRequestSentEvent',
'CircleRequestFulfilledEvent',
]

constructor(chains: ChainProvider[]) {
super(chains, CCTP_ROUTER_ADDRESS_MAP, SynapseCCTPRouter)
Expand Down
4 changes: 4 additions & 0 deletions packages/sdk-router/src/router/synapseRouterSet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ describe('SynapseRouterSet', () => {
it('Does not create SynapseRouter instances for chains without providers', () => {
expect(routerSet.routers[SupportedChainId.AVALANCHE]).toBeUndefined()
})

it('Correct bridge module name', () => {
expect(routerSet.bridgeModuleName).toEqual('SynapseBridge')
})
})

describe('getEstimatedTime', () => {
Expand Down
14 changes: 13 additions & 1 deletion packages/sdk-router/src/router/synapseRouterSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ import { MEDIAN_TIME_BRIDGE, ROUTER_ADDRESS_MAP } from '../constants'
* Wrapper class for interacting with a SynapseRouter contracts deployed on multiple chains.
*/
export class SynapseRouterSet extends RouterSet {
public readonly routerName = 'SynapseRouter'
public readonly bridgeModuleName = 'SynapseBridge'
public readonly allEvents = [
'DepositEvent',
'RedeemEvent',
'WithdrawEvent',
'MintEvent',
'DepositAndSwapEvent',
'MintAndSwapEvent',
'RedeemAndSwapEvent',
'RedeemAndRemoveEvent',
'WithdrawAndRemoveEvent',
'RedeemV2Event',
]

constructor(chains: ChainProvider[]) {
super(chains, ROUTER_ADDRESS_MAP, SynapseRouter)
Expand Down
1 change: 1 addition & 0 deletions packages/sdk-router/src/router/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export type BridgeQuote = {
originQuery: Query
destQuery: Query
estimatedTime: number
bridgeModuleName: string
}

/**
Expand Down
165 changes: 111 additions & 54 deletions packages/sdk-router/src/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_CCTP[SupportedChainId.ETH]
)
expect(result.bridgeModuleName).toEqual('SynapseCCTP')
})
})
})
Expand Down Expand Up @@ -306,6 +307,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_CCTP[SupportedChainId.ETH]
)
expect(result.bridgeModuleName).toEqual('SynapseCCTP')
})
})
})
Expand Down Expand Up @@ -344,6 +346,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.ETH]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -385,6 +388,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.AVALANCHE]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -419,6 +423,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.AVALANCHE]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -460,6 +465,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.ARBITRUM]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -494,6 +500,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_CCTP[SupportedChainId.ARBITRUM]
)
expect(result.bridgeModuleName).toEqual('SynapseCCTP')
})
})
})
Expand Down Expand Up @@ -536,6 +543,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.BSC]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -570,6 +578,7 @@ describe('SynapseSDK', () => {
expect(result.estimatedTime).toEqual(
MEDIAN_TIME_BRIDGE[SupportedChainId.BSC]
)
expect(result.bridgeModuleName).toEqual('SynapseBridge')
})
})
})
Expand Down Expand Up @@ -684,71 +693,119 @@ describe('SynapseSDK', () => {
)
})

describe('getBridgeModuleName', () => {
const synapse = new SynapseSDK([], [])

// https://github.com/synapsecns/synapse-contracts/blob/3f592a879baa4487a62ca8d2cfd44d329bc22e62/contracts/bridge/SynapseBridge.sol#L63-L121
describe('SynapseBridge events', () => {
const contractEvents = [
'TokenDeposit',
'TokenRedeem',
'TokenWithdraw',
'TokenMint',
'TokenDepositAndSwap',
'TokenMintAndSwap',
'TokenRedeemAndSwap',
'TokenRedeemAndRemove',
'TokenWithdrawAndRemove',
'TokenRedeemV2',
]

contractEvents.forEach((contractEvent) => {
it(contractEvent, () => {
// Event naming in contract and explorer is a bit different
// schema: TokenDeposit => DepositEvent
const explorerEvent = `${contractEvent.slice(5)}Event`
expect(synapse.getBridgeModuleName(explorerEvent)).toEqual(
'SynapseBridge'
)
})
})
})

// https://github.com/synapsecns/synapse-contracts/blob/3f592a879baa4487a62ca8d2cfd44d329bc22e62/contracts/cctp/events/SynapseCCTPEvents.sol#L5-L45
describe('SynapseCCTP events', () => {
const contractEvents = ['CircleRequestSent', 'CircleRequestFulfilled']

contractEvents.forEach((contractEvent) => {
it(contractEvent, () => {
// Event naming in contract and explorer is a bit different
// schema: CircleRequestSent => CircleRequestSentEvent
const explorerEvent = `${contractEvent}Event`
expect(synapse.getBridgeModuleName(explorerEvent)).toEqual(
'SynapseCCTP'
)
})
})
})

it('Throws when event name is unknown', () => {
expect(() => synapse.getBridgeModuleName('SomeUnknownEvent')).toThrow(
'Unknown event'
)
})
})

describe('getEstimatedTime', () => {
const synapse = new SynapseSDK(
[
SupportedChainId.ETH,
SupportedChainId.ARBITRUM,
SupportedChainId.MOONBEAM,
],
[ethProvider, arbProvider, moonbeamProvider]
[SupportedChainId.ETH, SupportedChainId.MOONBEAM],
[ethProvider, moonbeamProvider]
)

it('Returns estimated time for SynapseBridge', () => {
expect(
synapse.getEstimatedTime(
SupportedChainId.ETH,
ROUTER_ADDRESS_MAP[SupportedChainId.ETH]
)
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.ETH])
describe('Chain with a provider', () => {
it('Returns estimated time for SynapseBridge', () => {
expect(
synapse.getEstimatedTime(SupportedChainId.ETH, 'SynapseBridge')
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.ETH])

expect(
synapse.getEstimatedTime(
SupportedChainId.ARBITRUM,
ROUTER_ADDRESS_MAP[SupportedChainId.ARBITRUM]
)
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.ARBITRUM])
expect(
synapse.getEstimatedTime(SupportedChainId.MOONBEAM, 'SynapseBridge')
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.MOONBEAM])
})

expect(
synapse.getEstimatedTime(
SupportedChainId.MOONBEAM,
ROUTER_ADDRESS_MAP[SupportedChainId.MOONBEAM]
)
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.MOONBEAM])
})
it('Returns estimated time for SynapseCCTP', () => {
expect(
synapse.getEstimatedTime(SupportedChainId.ETH, 'SynapseCCTP')
).toEqual(MEDIAN_TIME_CCTP[SupportedChainId.ETH])
})

it('Returns estimated time for SynapseCCTP', () => {
expect(
synapse.getEstimatedTime(
SupportedChainId.ETH,
CCTP_ROUTER_ADDRESS_MAP[SupportedChainId.ETH]
)
).toEqual(MEDIAN_TIME_CCTP[SupportedChainId.ETH])
it('Throws when bridge module does not exist on a chain', () => {
expect(() =>
synapse.getEstimatedTime(SupportedChainId.MOONBEAM, 'SynapseCCTP')
).toThrow('No estimated time for chain 1284')
})

expect(
synapse.getEstimatedTime(
SupportedChainId.ARBITRUM,
CCTP_ROUTER_ADDRESS_MAP[SupportedChainId.ARBITRUM]
)
).toEqual(MEDIAN_TIME_CCTP[SupportedChainId.ARBITRUM])
it('Throws when bridge module name is invalid', () => {
expect(() =>
synapse.getEstimatedTime(SupportedChainId.ETH, 'SynapseSynapse')
).toThrow('Unknown bridge module')
})
})

it('Throws for chain without a provider', () => {
expect(() =>
synapse.getEstimatedTime(
SupportedChainId.AVALANCHE,
ROUTER_ADDRESS_MAP[SupportedChainId.AVALANCHE]
)
).toThrow('Unknown router address')
})
describe('Chain without a provider', () => {
it('Returns estimated time for SynapseBridge', () => {
expect(
synapse.getEstimatedTime(SupportedChainId.BSC, 'SynapseBridge')
).toEqual(MEDIAN_TIME_BRIDGE[SupportedChainId.BSC])
})

it('Throws for unknown router address', () => {
expect(() =>
synapse.getEstimatedTime(
SupportedChainId.MOONBEAM,
CCTP_ROUTER_ADDRESS_MAP[SupportedChainId.ETH]
)
).toThrow('Unknown router address')
it('Returns estimated time for SynapseCCTP', () => {
expect(
synapse.getEstimatedTime(SupportedChainId.ARBITRUM, 'SynapseCCTP')
).toEqual(MEDIAN_TIME_CCTP[SupportedChainId.ARBITRUM])
})

it('Throws when bridge module does not exist on a chain', () => {
expect(() =>
synapse.getEstimatedTime(SupportedChainId.DOGECHAIN, 'SynapseCCTP')
).toThrow('No estimated time for chain 2000')
})

it('Throws when bridge module name is invalid', () => {
expect(() =>
synapse.getEstimatedTime(SupportedChainId.BSC, 'SynapseSynapse')
).toThrow('Unknown bridge module')
})
})
})

Expand Down
1 change: 1 addition & 0 deletions packages/sdk-router/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class SynapseSDK {
// Define Bridge operations
public bridge = operations.bridge
public bridgeQuote = operations.bridgeQuote
public getBridgeModuleName = operations.getBridgeModuleName
public getEstimatedTime = operations.getEstimatedTime

public getBridgeGas = operations.getBridgeGas
Expand Down

0 comments on commit 9b87daa

Please sign in to comment.