diff --git a/packages/hop/src/Hop.test.ts b/packages/hop/src/Hop.test.ts index 0fe1a8a30..f8861b764 100644 --- a/packages/hop/src/Hop.test.ts +++ b/packages/hop/src/Hop.test.ts @@ -2,7 +2,12 @@ import { GreaterThanOrEqual, apply } from '@rabbitholegg/questdk/filter' import { describe, expect, test } from 'vitest' import { bridge } from './Hop.js' import { l1BridgeAbi, l2AmmWrapperAbi } from '@hop-protocol/core/abi' -import { DEPOSIT_ETH, DEPOSIT_ERC20, WITHDRAW_ETH, WITHDRAW_ERC20 } from './test-transactions.js' +import { + DEPOSIT_ETH, + DEPOSIT_ERC20, + WITHDRAW_ETH, + WITHDRAW_ERC20, +} from './test-transactions.js' const ETH_CHAIN_ID = 1 const POLYGON_CHAIN_ID = 137 const BASE_CHAIN_ID = 8453 @@ -19,10 +24,10 @@ import { parseEther } from 'viem' const TEST_USER = '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619' // Replace *project* with the name of the project describe('Given the optimism plugin', () => { - describe('When generating the filter', () => { test('should return a valid bridge action filter for L2 token tx', async () => { - const USDC_POLYGON_AMM_ADDRESS = '0x76b22b8C1079A44F1211D867D68b1eda76a635A7' + const USDC_POLYGON_AMM_ADDRESS = + '0x76b22b8C1079A44F1211D867D68b1eda76a635A7' const filter = await bridge({ sourceChainId: POLYGON_CHAIN_ID, destinationChainId: ETH_CHAIN_ID, @@ -46,7 +51,8 @@ describe('Given the optimism plugin', () => { }) test('should return a valid bridge action filter for L1 token tx', async () => { - const USDC_MAINNET_BRIDGE_ADDRESS = '0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a' + const USDC_MAINNET_BRIDGE_ADDRESS = + '0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a' const filter = await bridge({ sourceChainId: ETH_CHAIN_ID, destinationChainId: POLYGON_CHAIN_ID, @@ -70,7 +76,8 @@ describe('Given the optimism plugin', () => { }) test('should return a valid bridge action filter for L1 ETH tx', async () => { - const ETH_MAINNET_BRIDGE_ADDRESS = '0xb8901acB165ed027E32754E0FFe830802919727f' + const ETH_MAINNET_BRIDGE_ADDRESS = + '0xb8901acB165ed027E32754E0FFe830802919727f' const filter = await bridge({ sourceChainId: ETH_CHAIN_ID, destinationChainId: POLYGON_CHAIN_ID, @@ -123,7 +130,7 @@ describe('Given the optimism plugin', () => { destinationChainId: ETH_CHAIN_ID, tokenAddress: WETH_ADDRESS_POLYGON, amount: GreaterThanOrEqual(parseEther('9')), - recipient: '0x467B79AAfD7977F6d1E772e0b121047AC655C389' + recipient: '0x467B79AAfD7977F6d1E772e0b121047AC655C389', }) expect(apply(transaction, filter)).to.be.true }) @@ -134,11 +141,11 @@ describe('Given the optimism plugin', () => { destinationChainId: ARBITRUM_ONE_CHAIN_ID, tokenAddress: HOP_ADDRESS_POLYGON, amount: GreaterThanOrEqual(parseEther('4952')), - recipient: '0x34327028C727613872cd80122B9a489a4B9C0bFA' + recipient: '0x34327028C727613872cd80122B9a489a4B9C0bFA', }) expect(apply(transaction, filter)).to.be.true }) }) test('should not pass filter with invalid transactions', () => {}) -}) \ No newline at end of file +}) diff --git a/packages/hop/src/Hop.ts b/packages/hop/src/Hop.ts index b1d87cb45..ed79b4234 100644 --- a/packages/hop/src/Hop.ts +++ b/packages/hop/src/Hop.ts @@ -1,84 +1,107 @@ - import { mainnet as addresses } from '@hop-protocol/core/addresses' import { mainnet } from '@hop-protocol/core/networks' import { utils } from '@hop-protocol/sdk' import { type BridgeActionParams, compressJson } from '@rabbitholegg/questdk' -import { type Bridges, type Bridge, type L2BridgeProps, type L1BridgeProps } from './types.js' -import { l1BridgeAbi, l2AmmWrapperAbi, l2BridgeAbi } from '@hop-protocol/core/abi' +import { + type Bridges, + type Bridge, + type L2BridgeProps, + type L1BridgeProps, +} from './types.js' +import { + l1BridgeAbi, + l2AmmWrapperAbi, + l2BridgeAbi, +} from '@hop-protocol/core/abi' import { type Address } from 'viem' const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' export const bridge = (bridge: BridgeActionParams) => { // This is the information we'll use to compose the Transaction object - const { - sourceChainId, - destinationChainId, - tokenAddress, - amount, - recipient, - } = bridge + const { sourceChainId, destinationChainId, tokenAddress, amount, recipient } = + bridge const bridges = addresses.bridges as Bridges - const chainSlug = utils.chainIdToSlug("mainnet", sourceChainId); - if(sourceChainId === 1) { + const chainSlug = utils.chainIdToSlug('mainnet', sourceChainId) + if (sourceChainId === 1) { const bridgeData: [string, Bridge] = Object.entries(bridges).filter( - ([, bridge]: [string, Bridge] ) => - { - if(bridge[chainSlug]) return (bridge[chainSlug] as L1BridgeProps).l1CanonicalToken === tokenAddress + ([, bridge]: [string, Bridge]) => { + if (bridge[chainSlug]) + return ( + (bridge[chainSlug] as L1BridgeProps).l1CanonicalToken === + tokenAddress + ) else return false - })[0] - const bridgeProps = (bridgeData[1][chainSlug] as L1BridgeProps) + }, + )[0] + const bridgeProps = bridgeData[1][chainSlug] as L1BridgeProps const contractTarget = bridgeProps.l1Bridge // We always want to return a compressed JSON object which we'll transform into a TransactionFilter return compressJson({ chainId: sourceChainId, // The chainId of the source chain - to: contractTarget, // The contract address of the bridge + to: contractTarget, // The contract address of the bridge input: { $abi: l1BridgeAbi, // The ABI of the bridge contract chainId: destinationChainId, // The chainId of the destination chain recipient: recipient, // The recipient of the bridged tokens amount: amount, // The amount of tokens to bridge - }, // The input object is where we'll put the ABI and the parameters + }, // The input object is where we'll put the ABI and the parameters }) } else { - const bridgeData: [string, Bridge] = Object.entries(bridges).filter(([, bridge]: [string, Bridge] ) => { - if(bridge[chainSlug]) return (bridge[chainSlug] as L2BridgeProps).l2CanonicalToken === tokenAddress - else return false - })[0] - const bridgeProps = (bridgeData[1][chainSlug] as L2BridgeProps) + const bridgeData: [string, Bridge] = Object.entries(bridges).filter( + ([, bridge]: [string, Bridge]) => { + if (bridge[chainSlug]) + return ( + (bridge[chainSlug] as L2BridgeProps).l2CanonicalToken === + tokenAddress + ) + else return false + }, + )[0] + const bridgeProps = bridgeData[1][chainSlug] as L2BridgeProps // Currently only HOP lacks an AMM wrapper const hasAMM = bridgeProps.l2AmmWrapper !== ZERO_ADDRESS // If there is an AMM wrapper we want to target that, otherwise we target the L2 bridge - const contractTarget = hasAMM ? bridgeProps.l2AmmWrapper : bridgeProps.l2Bridge - const abi = hasAMM ? l2AmmWrapperAbi: l2BridgeAbi + const contractTarget = hasAMM + ? bridgeProps.l2AmmWrapper + : bridgeProps.l2Bridge + const abi = hasAMM ? l2AmmWrapperAbi : l2BridgeAbi return compressJson({ chainId: sourceChainId, // The chainId of the source chain - to: contractTarget, // The contract address of the bridge + to: contractTarget, // The contract address of the bridge input: { $abi: abi, // The ABI of the bridge contract, if hop is the target token this is the bridge, otherwise it should be the AMM chainId: destinationChainId, // The chainId of the destination chain recipient: recipient, // The recipient of the bridged tokens amount: amount, // The amount of tokens to bridge - }, // The input object is where we'll put the ABI and the parameters + }, // The input object is where we'll put the ABI and the parameters }) } } // https://github.com/hop-protocol/hop/blob/develop/packages/core/src/metadata/tokens.ts -export const getSupportedTokenAddresses = async (_chainId: number): Promise
=> { - const chainSlug = utils.chainIdToSlug("mainnet", _chainId); +export const getSupportedTokenAddresses = async ( + _chainId: number, +): Promise => { + const chainSlug = utils.chainIdToSlug('mainnet', _chainId) // For each entry in bridge take the token address [ l1CanonicalToken or l2CanonicalToken ] - if(addresses && addresses.bridges) { - return Object.entries(addresses.bridges! as Bridges).map(([, bridge]: [string, Bridge] ) => { - // Find the bridge element whose key matches the chainSlug and return the token address - if(bridge && (bridge as any)[chainSlug]) - return _chainId === 1 ? (bridge[chainSlug] as L1BridgeProps).l1CanonicalToken : (bridge[chainSlug] as L2BridgeProps).l2CanonicalToken - return '0x0' - }) as Address[] + if (addresses && addresses.bridges) { + return Object.entries(addresses.bridges! as Bridges).map( + ([, bridge]: [string, Bridge]) => { + // Find the bridge element whose key matches the chainSlug and return the token address + if (bridge && (bridge as any)[chainSlug]) + return _chainId === 1 + ? (bridge[chainSlug] as L1BridgeProps).l1CanonicalToken + : (bridge[chainSlug] as L2BridgeProps).l2CanonicalToken + return '0x0' + }, + ) as Address[] } return [] as Address[] } // https://github.com/hop-protocol/hop/blob/develop/packages/core/src/networks/mainnet.ts export const getSupportedChainIds = async () => { - return Object.entries(mainnet).map(([, chain]) => { return chain.networkId }) + return Object.entries(mainnet).map(([, chain]) => { + return chain.networkId + }) } diff --git a/packages/hop/src/index.ts b/packages/hop/src/index.ts index d44b6d4bd..cd867a214 100644 --- a/packages/hop/src/index.ts +++ b/packages/hop/src/index.ts @@ -1,5 +1,5 @@ // This file is standard for more projects. -// The main degree of nuance is in the +// The main degree of nuance is in the import { type IActionPlugin, diff --git a/packages/hop/src/test-transactions.ts b/packages/hop/src/test-transactions.ts index 378b025dd..311dad2af 100644 --- a/packages/hop/src/test-transactions.ts +++ b/packages/hop/src/test-transactions.ts @@ -1,88 +1,96 @@ export const DEPOSIT_ETH = { - type: "eip1559", - blockHash: "0xb4dd9383be530e080ca83413c949d631a8a9ec9bb70f8d5a4bf9510700c65c8d", - blockNumber: "18037758", - from: "0xf9F31dc3399032304c6bdf93f3e028F70edD22A6", - gas: "500000", - hash: "0x258b3b80026142aee072879589776e4204e60ba6e134213c20c77bdebd3de310", - input: "0xdeace8f50000000000000000000000000000000000000000000000000000000000002105000000000000000000000000f9f31dc3399032304c6bdf93f3e028f70edd22a60000000000000000000000000000000000000000000000000870c2ad786480000000000000000000000000000000000000000000000000000865155c13dad2c70000000000000000000000000000000000000000000000000000000064fa590e000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000", + type: 'eip1559', + blockHash: + '0xb4dd9383be530e080ca83413c949d631a8a9ec9bb70f8d5a4bf9510700c65c8d', + blockNumber: '18037758', + from: '0xf9F31dc3399032304c6bdf93f3e028F70edD22A6', + gas: '500000', + hash: '0x258b3b80026142aee072879589776e4204e60ba6e134213c20c77bdebd3de310', + input: + '0xdeace8f50000000000000000000000000000000000000000000000000000000000002105000000000000000000000000f9f31dc3399032304c6bdf93f3e028f70edd22a60000000000000000000000000000000000000000000000000870c2ad786480000000000000000000000000000000000000000000000000000865155c13dad2c70000000000000000000000000000000000000000000000000000000064fa590e000000000000000000000000710bda329b2a6224e4b44833de30f38e7f81d5640000000000000000000000000000000000000000000000000000000000000000', nonce: 111, - to: "0xb8901acB165ed027E32754E0FFe830802919727f", + to: '0xb8901acB165ed027E32754E0FFe830802919727f', transactionIndex: 89, - value: "608200000000000000", - v: "0", - r: "0x9e4d30eaa9aa01abb5fdd6bfc55d76f631eecf0e907b2e9ddcdc65178f2445a8", - s: "0x48fd6d2fc47671e7077b9382b020ecaa73e672dfee5fac29ee73dd1e8913aa7a", - gasPrice: "15581177775", - maxFeePerGas: "21941686631", - maxPriorityFeePerGas: "100000000", + value: '608200000000000000', + v: '0', + r: '0x9e4d30eaa9aa01abb5fdd6bfc55d76f631eecf0e907b2e9ddcdc65178f2445a8', + s: '0x48fd6d2fc47671e7077b9382b020ecaa73e672dfee5fac29ee73dd1e8913aa7a', + gasPrice: '15581177775', + maxFeePerGas: '21941686631', + maxPriorityFeePerGas: '100000000', chainId: 1, accessList: [], - typeHex: "0x2" + typeHex: '0x2', } export const DEPOSIT_ERC20 = { - type: "eip1559", - blockHash: "0x09f178f6cca7b4fb96da67133cee4e9c057166f38aa03d11cc1408d8f01d28eb", - blockNumber: "18037423", - from: "0x1a929b5d550c45ca8a0cfdb82f7c9c15fc278f31", - gas: "500000", - hash: "0xfc7db0dd322eb0cfa91468aaf580cf2448ac4782e4364657c954659b380531d8", - input: "0xdeace8f5000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000001a929b5d550c45ca8a0cfdb82f7c9c15fc278f31000000000000000000000000000000000000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000ed4252f0000000000000000000000000000000000000000000000000000000064fa495b000000000000000000000000a6a688f107851131f0e1dce493ebbebfaf99203e0000000000000000000000000000000000000000000000000000000000000000", + type: 'eip1559', + blockHash: + '0x09f178f6cca7b4fb96da67133cee4e9c057166f38aa03d11cc1408d8f01d28eb', + blockNumber: '18037423', + from: '0x1a929b5d550c45ca8a0cfdb82f7c9c15fc278f31', + gas: '500000', + hash: '0xfc7db0dd322eb0cfa91468aaf580cf2448ac4782e4364657c954659b380531d8', + input: + '0xdeace8f5000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000001a929b5d550c45ca8a0cfdb82f7c9c15fc278f31000000000000000000000000000000000000000000000000000000000ee6b280000000000000000000000000000000000000000000000000000000000ed4252f0000000000000000000000000000000000000000000000000000000064fa495b000000000000000000000000a6a688f107851131f0e1dce493ebbebfaf99203e0000000000000000000000000000000000000000000000000000000000000000', nonce: 2886, - to: "0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a", + to: '0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a', transactionIndex: 96, - value: "0", - v: "0", - r: "0x11010d9a321ef8109ddf2b86125925d654f014592b06c175725fc270d9492255", - s: "0x7f197da3325678ce826f0fc8b6c89975dd8fda85b56e294969982a9b1780ef51", - gasPrice: "27807295579", - maxFeePerGas: "40561948008", - maxPriorityFeePerGas: "100000000", + value: '0', + v: '0', + r: '0x11010d9a321ef8109ddf2b86125925d654f014592b06c175725fc270d9492255', + s: '0x7f197da3325678ce826f0fc8b6c89975dd8fda85b56e294969982a9b1780ef51', + gasPrice: '27807295579', + maxFeePerGas: '40561948008', + maxPriorityFeePerGas: '100000000', chainId: 1, accessList: [], - typeHex: "0x2" + typeHex: '0x2', } // USDC Deposit export const WITHDRAW_ETH = { - blockHash: "0xf7be1c972fe3d590384a4ba1880a41f29aa3a586003c1a670899f3152788b60c", - blockNumber: "46990773", - from: "0x467b79aafd7977f6d1e772e0b121047ac655c389", - gas: "1000000", - gasPrice: "111331964693", - maxPriorityFeePerGas: "31623531250", - maxFeePerGas: "157613916701", - hash: "0x591a4ff799506dd3040ac8aaa5d4d7b1eed20441bba3dba0af7d7827c30c082d", - input: "0xeea0d7b20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000467b79aafd7977f6d1e772e0b121047ac655c3890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000001eae15797b98e300000000000000000000000000000000000000000000000089dec43b3aee98fd0000000000000000000000000000000000000000000000000000000064fa40d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + blockHash: + '0xf7be1c972fe3d590384a4ba1880a41f29aa3a586003c1a670899f3152788b60c', + blockNumber: '46990773', + from: '0x467b79aafd7977f6d1e772e0b121047ac655c389', + gas: '1000000', + gasPrice: '111331964693', + maxPriorityFeePerGas: '31623531250', + maxFeePerGas: '157613916701', + hash: '0x591a4ff799506dd3040ac8aaa5d4d7b1eed20441bba3dba0af7d7827c30c082d', + input: + '0xeea0d7b20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000467b79aafd7977f6d1e772e0b121047ac655c3890000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000000000000000000000000000001eae15797b98e300000000000000000000000000000000000000000000000089dec43b3aee98fd0000000000000000000000000000000000000000000000000000000064fa40d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', nonce: 130609, - to: "0xc315239cFb05F1E130E7E28E603CEa4C014c57f0", + to: '0xc315239cFb05F1E130E7E28E603CEa4C014c57f0', transactionIndex: 63, - value: "0", - type: "eip1559", + value: '0', + type: 'eip1559', accessList: [], chainId: 137, - v: "1", - r: "0x9a197c94bf430e99db8924ee1005be7079e9fc6a5c2b48af11f276f96b2cef07", - s: "0x7097fbabca87fddf7d996ad461e57f9a555af71c62ac6e618acc43fbaf2f5573", - typeHex: "0x2" + v: '1', + r: '0x9a197c94bf430e99db8924ee1005be7079e9fc6a5c2b48af11f276f96b2cef07', + s: '0x7097fbabca87fddf7d996ad461e57f9a555af71c62ac6e618acc43fbaf2f5573', + typeHex: '0x2', } export const WITHDRAW_ERC20 = { - blockHash: "0xd720d8ba488c631905489f6405c25c024f54dc29281b69b92753defe4a81219e", - blockNumber: "46987654", - from: "0x34327028c727613872cd80122b9a489a4b9c0bfa", - gas: "1000000", - gasPrice: "119547089604", - maxPriorityFeePerGas: "30000000000", - maxFeePerGas: "152760064977", - hash: "0x07b1c5480d3f8bbdad7fe20c7f05f41f3415c3d9d72413743beb651ca822ead9", - input: "0xa6bd1b33000000000000000000000000000000000000000000000000000000000000a4b100000000000000000000000034327028c727613872cd80122b9a489a4b9c0bfa00000000000000000000000000000000000000000000010c7866c5444ef9c32c000000000000000000000000000000000000000000000000d0430dcada41d1ab00000000000000000000000000000000000000000000010a507f5ce35ab9412b0000000000000000000000000000000000000000000000000000000064fa25e3", + blockHash: + '0xd720d8ba488c631905489f6405c25c024f54dc29281b69b92753defe4a81219e', + blockNumber: '46987654', + from: '0x34327028c727613872cd80122b9a489a4b9c0bfa', + gas: '1000000', + gasPrice: '119547089604', + maxPriorityFeePerGas: '30000000000', + maxFeePerGas: '152760064977', + hash: '0x07b1c5480d3f8bbdad7fe20c7f05f41f3415c3d9d72413743beb651ca822ead9', + input: + '0xa6bd1b33000000000000000000000000000000000000000000000000000000000000a4b100000000000000000000000034327028c727613872cd80122b9a489a4b9c0bfa00000000000000000000000000000000000000000000010c7866c5444ef9c32c000000000000000000000000000000000000000000000000d0430dcada41d1ab00000000000000000000000000000000000000000000010a507f5ce35ab9412b0000000000000000000000000000000000000000000000000000000064fa25e3', nonce: 10584, - to: "0x58c61AeE5eD3D748a1467085ED2650B697A66234", + to: '0x58c61AeE5eD3D748a1467085ED2650B697A66234', transactionIndex: 58, - value: "0", - type: "eip1559", + value: '0', + type: 'eip1559', accessList: [], chainId: 137, - v: "1", - r: "0x46d84c1a0ec3d37955bc8ef8ee64bfbe804e01a6b9cb4c249e17bdb1d1a9f3d1", - s: "0x228ae7690652f9d18bb97e4a76cb6b430fdabe193b69df29764341cc3b70b570", - typeHex: "0x2" -} // HOP Withdrawal \ No newline at end of file + v: '1', + r: '0x46d84c1a0ec3d37955bc8ef8ee64bfbe804e01a6b9cb4c249e17bdb1d1a9f3d1', + s: '0x228ae7690652f9d18bb97e4a76cb6b430fdabe193b69df29764341cc3b70b570', + typeHex: '0x2', +} // HOP Withdrawal diff --git a/packages/hop/src/types.ts b/packages/hop/src/types.ts index e1447bc30..5e4a2d5a6 100644 --- a/packages/hop/src/types.ts +++ b/packages/hop/src/types.ts @@ -23,4 +23,4 @@ export interface Bridge { export type Bridges = { [tokenSymbol: string]: Bridge -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b87ab16b3..4bb074501 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5014,7 +5014,6 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true /global@4.4.0: resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} @@ -7623,7 +7622,7 @@ packages: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} hasBin: true dependencies: - glob: 7.2.0 + glob: 7.2.3 dev: false /rimraf@3.0.2: @@ -9691,7 +9690,7 @@ packages: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} requiresBuild: true dependencies: - string-width: 1.0.2 + string-width: 4.2.3 dev: false optional: true