Skip to content

Commit

Permalink
Merge pull request #10 from rabbitholegg/jim/support-eth-wrap-multisend
Browse files Browse the repository at this point in the history
Feat(connext): Add support for multisend ETH bridge
  • Loading branch information
jimobrien authored Aug 23, 2023
2 parents a79078b + 00233bc commit 5a01539
Show file tree
Hide file tree
Showing 8 changed files with 419 additions and 99 deletions.
5 changes: 5 additions & 0 deletions .changeset/sour-boxes-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rabbitholegg/questdk-plugin-connext": minor
---

Support wrapping ETH on Connext bridge
36 changes: 18 additions & 18 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ jobs:
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

size:
name: Size
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Clone repository
uses: actions/checkout@v3

- name: Install dependencies
uses: ./.github/actions/install-dependencies
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Report build size
uses: preactjs/compressed-size-action@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# size:
# name: Size
# runs-on: ubuntu-latest
# timeout-minutes: 5

# steps:
# - name: Clone repository
# uses: actions/checkout@v3

# - name: Install dependencies
# uses: ./.github/actions/install-dependencies
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# - name: Report build size
# uses: preactjs/compressed-size-action@v2
# with:
# repo-token: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions packages/connext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"vitest": "^0.33.0"
},
"dependencies": {
"@connext/nxtp-txservice": "^2.0.0",
"@connext/nxtp-utils": "^2.0.4",
"@connext/smart-contracts": "^2.0.0",
"@rabbitholegg/questdk": "1.0.1-alpha.8",
Expand Down
68 changes: 67 additions & 1 deletion packages/connext/src/Connext.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { XCALL_ABI_FRAGMENTS, bridge } from './Connext.js'
import { getDeployedMultisendContract } from '@connext/nxtp-txservice'
import { MultisendAbi } from '@connext/nxtp-utils'
import { GreaterThanOrEqual, apply } from '@rabbitholegg/questdk/filter'
import { describe, expect, test } from 'vitest'
import { bridge } from './Connext.js'
import { XCALL_ABI_FRAGMENTS } from './abi.js'

describe('Connext', () => {
const ETH = '0x0000000000000000000000000000000000000000'
const OP_WETH = '0x4200000000000000000000000000000000000006'

describe('Bridge', () => {
const USDC = '0x7F5c764cBc14f9669B88837ca1490cCa17c31607'

Expand All @@ -29,6 +35,31 @@ describe('Connext', () => {
})
})

test('should use the WETH wrapper multisend contract when bridging ETH', async () => {
const filter = await bridge({
sourceChainId: 10,
destinationChainId: 137,
tokenAddress: ETH,
amount: GreaterThanOrEqual(100000n),
})

const multiSendContract = getDeployedMultisendContract(10)

expect(filter).to.deep.equal({
chainId: '0xa',
to: multiSendContract?.address,
value: {
$gte: '100000',
},
input: {
$abi: MultisendAbi,
transactions: {
$regex: OP_WETH.slice(2),
},
},
})
})

describe('Apply filter', () => {
test('transaction should pass filter', async () => {
const transaction = {
Expand Down Expand Up @@ -64,5 +95,40 @@ describe('Connext', () => {

expect(apply(transaction, filter)).to.be.true
})

test('ETH bridge should pass filter', async () => {
const transaction = {
blockHash:
'0xfdb722e4a99e3422490bc12d15fafab54ebb7e2e83ff08e9fe20d70045e94889',
blockNumber: '0x67812f1',
from: '0xa4c8bb4658bc44bac430699c8b7b13dab28e0f4e',
gas: '0x8820a',
gasPrice: '0x1167',
maxFeePerGas: '0x11e2',
maxPriorityFeePerGas: '0x112d',
hash: '0xb8e2c0baf137b64553c91f286bde62cc37275d0b9f9d3e6c0041c6be79de45af',
input:
'0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000026b004200000000000000000000000000000000000006000000000000000000000000000000000000000000000000008af8a1fa5fcc180000000000000000000000000000000000000000000000000000000000000004d0e30db000420000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044095ea7b30000000000000000000000008f7492de823025b4cfaab1d34c58963f2af5deda000000000000000000000000000000000000000000000000008af8a1fa5fcc18008f7492de823025b4cfaab1d34c58963f2af5deda0000000000000000000000000000000000000000000000000026aa1a3465338400000000000000000000000000000000000000000000000000000000000001248aac16ba0000000000000000000000000000000000000000000000000000000000657468000000000000000000000000268682b7d9992ae7e2ca4a8bcc9d9655fb06056f0000000000000000000000004200000000000000000000000000000000000006000000000000000000000000a4c8bb4658bc44bac430699c8b7b13dab28e0f4e000000000000000000000000000000000000000000000000008af8a1fa5fcc18000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000a4c8bb4658bc44bac430699c8b7b13dab28e0f4e000000000000000000000000000000000000000000',
nonce: '0x45',
to: '0xb0eef3e1de973d045c3858e072c540299585252d',
transactionIndex: '0x6',
value: '0xb1a2bc2ec4ff9c',
type: '0x2',
accessList: [],
chainId: '0xa',
v: '0x1',
r: '0x11d6fd962cf4090c1464404492a36ef7323c6173908883c87fbc695219e6d026',
s: '0x739b7240f8dd466881a66971b476f07c2fcce9a6f519d3c06f95133ad983092e',
}

const filter = await bridge({
sourceChainId: 10,
destinationChainId: 137,
tokenAddress: ETH,
amount: GreaterThanOrEqual(1000000000000000n),
})

expect(apply(transaction, filter)).to.be.true
})
})
})
105 changes: 66 additions & 39 deletions packages/connext/src/Connext.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { ConnextContract } from './contract-addresses.js'
import { getDeployedMultisendContract } from '@connext/nxtp-txservice'
import {
type ChainData,
MultisendAbi,
chainIdToDomain,
domainToChainId,
getChainData,
} from '@connext/nxtp-utils'
import { type BridgeActionParams, compressJson } from '@rabbitholegg/questdk'
import {
type BridgeActionParams,
type TransactionFilter,
compressJson,
} from '@rabbitholegg/questdk'
import { type Address, toHex } from 'viem'
import { XCALL_ABI_FRAGMENTS } from './abi.js'
import { ConnextContract } from './contract-addresses.js'

let _chainDataCache: Map<string, ChainData> | null = null

const ETH_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000'

const _getChainData = async () => {
if (!_chainDataCache) {
const chainData = await getChainData()
Expand All @@ -19,42 +28,25 @@ const _getChainData = async () => {
return _chainDataCache
}

export const XCALL_ABI_FRAGMENTS = [
{
inputs: [
{ internalType: 'uint32', name: '_destination', type: 'uint32' },
{ internalType: 'address', name: '_to', type: 'address' },
{ internalType: 'address', name: '_asset', type: 'address' },
{ internalType: 'address', name: '_delegate', type: 'address' },
{ internalType: 'uint256', name: '_amount', type: 'uint256' },
{ internalType: 'uint256', name: '_slippage', type: 'uint256' },
{ internalType: 'bytes', name: '_callData', type: 'bytes' },
],
name: 'xcall',
outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
stateMutability: 'payable',
type: 'function',
},
// This overloaded function is not found in the Connext ABI json for some reason
{
inputs: [
{ internalType: 'uint32', name: '_destination', type: 'uint32' },
{ internalType: 'address', name: '_to', type: 'address' },
{ internalType: 'address', name: '_asset', type: 'address' },
{ internalType: 'address', name: '_delegate', type: 'address' },
{ internalType: 'uint256', name: '_amount', type: 'uint256' },
{ internalType: 'uint256', name: '_slippage', type: 'uint256' },
{ internalType: 'bytes', name: '_callData', type: 'bytes' },
{ internalType: 'uint256', name: '_relayerFee', type: 'uint256' },
],
name: 'xcall',
outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
stateMutability: 'payable',
type: 'function',
},
]

export const bridge = async (bridge: BridgeActionParams) => {
export const getWETHAddress = async (chainId: number) => {
const chains = await _getChainData()
const domainId = chainIdToDomain(chainId)
const chainData = chains?.get(String(domainId))
const assets = Object.keys(chainData?.assetId || {})

let wethAddress
for (const address of assets) {
if (chainData?.assetId[address].symbol === 'WETH') {
wethAddress = address
break
}
}
return wethAddress
}

export const bridge = async (
bridge: BridgeActionParams,
): Promise<TransactionFilter> => {
const {
sourceChainId,
destinationChainId,
Expand All @@ -66,8 +58,43 @@ export const bridge = async (bridge: BridgeActionParams) => {

const defaultContractAddress = ConnextContract[sourceChainId]
const destinationDomain = chainIdToDomain(destinationChainId)
const requiresWrapperMultisend = tokenAddress === ETH_TOKEN_ADDRESS

/*
Connext uses a MultiSend to wrap when briding ETH.
https://github.com/connext/monorepo/issues/2905
https://github.com/connext/monorepo/issues/4218
Contract addresses: https://github.com/search?q=repo%3Aconnext%2Fmonorepo%20MultiSend.json&type=code
*/

if (requiresWrapperMultisend) {
const multiSendContract = getDeployedMultisendContract(sourceChainId)

if (!multiSendContract) {
throw new Error(
`No multisend contract deployed on chain ${sourceChainId}`,
)
}

const wethAddress = await getWETHAddress(sourceChainId)

if (!wethAddress) {
throw new Error(`No WETH address found on chain ${sourceChainId}`)
}

return compressJson({
chainId: toHex(sourceChainId),
to: multiSendContract.address,
value: amount,
input: {
$abi: MultisendAbi,
transactions: {
$regex: wethAddress.slice(2),
},
},
})
}

// https://docs.connext.network/developers/reference/contracts/calls#xcall
return compressJson({
chainId: toHex(sourceChainId),
to: contractAddress || defaultContractAddress,
Expand Down
35 changes: 35 additions & 0 deletions packages/connext/src/abi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// https://docs.connext.network/developers/reference/contracts/calls#xcall
export const XCALL_ABI_FRAGMENTS = [
{
inputs: [
{ internalType: 'uint32', name: '_destination', type: 'uint32' },
{ internalType: 'address', name: '_to', type: 'address' },
{ internalType: 'address', name: '_asset', type: 'address' },
{ internalType: 'address', name: '_delegate', type: 'address' },
{ internalType: 'uint256', name: '_amount', type: 'uint256' },
{ internalType: 'uint256', name: '_slippage', type: 'uint256' },
{ internalType: 'bytes', name: '_callData', type: 'bytes' },
],
name: 'xcall',
outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
stateMutability: 'payable',
type: 'function',
},
// This overloaded function is not found in the Connext ABI json for some reason
{
inputs: [
{ internalType: 'uint32', name: '_destination', type: 'uint32' },
{ internalType: 'address', name: '_to', type: 'address' },
{ internalType: 'address', name: '_asset', type: 'address' },
{ internalType: 'address', name: '_delegate', type: 'address' },
{ internalType: 'uint256', name: '_amount', type: 'uint256' },
{ internalType: 'uint256', name: '_slippage', type: 'uint256' },
{ internalType: 'bytes', name: '_callData', type: 'bytes' },
{ internalType: 'uint256', name: '_relayerFee', type: 'uint256' },
],
name: 'xcall',
outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
stateMutability: 'payable',
type: 'function',
},
]
Loading

0 comments on commit 5a01539

Please sign in to comment.