Skip to content

Commit 00428c2

Browse files
committed
add adapter and tests for eco
1 parent d79b249 commit 00428c2

File tree

10 files changed

+297
-26
lines changed

10 files changed

+297
-26
lines changed

packages/sdk/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"lint:fix": "yarn lint:check --fix",
1818
"pre-commit": "lint-staged",
1919
"test": "hardhat test",
20-
"test:next": "vitest test-next/proveMessage.spec.ts",
20+
"test:next": "vitest run",
2121
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json",
2222
"autogen:docs": "typedoc --out docs src/index.ts"
2323
},
@@ -48,7 +48,9 @@
4848
"typedoc": "^0.22.13",
4949
"mocha": "^10.0.0",
5050
"vitest": "^0.28.3",
51-
"zod": "^3.11.6"
51+
"zod": "^3.11.6",
52+
"viem": "^0.3.30",
53+
"isomorphic-fetch": "^3.0.0"
5254
},
5355
"dependencies": {
5456
"@eth-optimism/contracts": "0.5.40",

packages/sdk/setupVitest.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import fetch from 'isomorphic-fetch'
2+
3+
// viem needs this
4+
global.fetch = fetch
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
import { Contract } from 'ethers'
3+
import { hexStringEquals } from '@eth-optimism/core-utils'
4+
5+
import { AddressLike } from '../interfaces'
6+
import { toAddress } from '../utils'
7+
import { StandardBridgeAdapter } from './standard-bridge'
8+
9+
/**
10+
* Bridge adapter for ECO.
11+
* ECO bridge requires a separate adapter as exposes different functions than our standard bridge
12+
*/
13+
export class ECOBridgeAdapter extends StandardBridgeAdapter {
14+
public async supportsTokenPair(
15+
l1Token: AddressLike,
16+
l2Token: AddressLike
17+
): Promise<boolean> {
18+
const l1Bridge = new Contract(
19+
this.l1Bridge.address,
20+
[
21+
{
22+
inputs: [],
23+
name: 'ecoAddress',
24+
outputs: [
25+
{
26+
internalType: 'address',
27+
name: '',
28+
type: 'address',
29+
},
30+
],
31+
stateMutability: 'view',
32+
type: 'function',
33+
},
34+
],
35+
this.messenger.l1Provider
36+
)
37+
38+
const l2Bridge = new Contract(
39+
this.l2Bridge.address,
40+
[
41+
{
42+
inputs: [],
43+
name: 'l2EcoToken',
44+
outputs: [
45+
{
46+
internalType: 'contract L2ECO',
47+
name: '',
48+
type: 'address',
49+
},
50+
],
51+
stateMutability: 'view',
52+
type: 'function',
53+
},
54+
],
55+
this.messenger.l2Provider
56+
)
57+
58+
const [remoteL1Token, remoteL2Token] = await Promise.all([
59+
l1Bridge.ecoAddress(),
60+
l2Bridge.l2EcoToken(),
61+
])
62+
63+
if (!hexStringEquals(remoteL1Token, toAddress(l1Token))) {
64+
return false
65+
}
66+
67+
if (!hexStringEquals(remoteL2Token, toAddress(l2Token))) {
68+
return false
69+
}
70+
71+
return true
72+
}
73+
}

packages/sdk/src/adapters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './standard-bridge'
22
export * from './eth-bridge'
33
export * from './dai-bridge'
4+
export * from './eco-bridge'

packages/sdk/src/utils/chain-constants.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import {
1212
OEL2ContractsLike,
1313
BridgeAdapterData,
1414
} from '../interfaces'
15-
import { StandardBridgeAdapter, DAIBridgeAdapter } from '../adapters'
15+
import {
16+
StandardBridgeAdapter,
17+
DAIBridgeAdapter,
18+
ECOBridgeAdapter,
19+
} from '../adapters'
1620

1721
export const DEPOSIT_CONFIRMATION_BLOCKS: {
1822
[ChainID in L2ChainID]: number
@@ -206,7 +210,7 @@ export const BRIDGE_ADAPTER_DATA: {
206210
l2Bridge: '0x467194771dAe2967Aef3ECbEDD3Bf9a310C76C65' as const,
207211
},
208212
ECO: {
209-
Adapter: StandardBridgeAdapter,
213+
Adapter: ECOBridgeAdapter,
210214
l1Bridge: '0x7a01E277B8fDb8CDB2A2258508514716359f44A0' as const,
211215
l2Bridge: '0x7a01E277B8fDb8CDB2A2258508514716359f44A0' as const,
212216
},
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import ethers from 'ethers'
2+
import { describe, expect, it } from 'vitest'
3+
import {
4+
l1TestClient,
5+
l2TestClient,
6+
l1PublicClient,
7+
l2PublicClient,
8+
} from './testUtils/viemClients'
9+
10+
import { BRIDGE_ADAPTER_DATA, CrossChainMessenger, DEPOSIT_CONFIRMATION_BLOCKS, L2ChainID } from '../src'
11+
import { Address, PublicClient, parseEther } from 'viem'
12+
import { l1Provider, l2Provider } from './testUtils/ethersProviders'
13+
14+
const ECO_WHALE: Address = '0xBd11c836279a1352ce737FbBFba36b20734B04e7'
15+
16+
// TODO: use tokenlist as source of truth
17+
const ECO_L1_TOKEN_ADDRESS: Address = '0x3E87d4d9E69163E7590f9b39a70853cf25e5ABE3'
18+
const ECO_L2_TOKEN_ADDRESS: Address = '0x54bBECeA38ff36D32323f8A754683C1F5433A89f'
19+
20+
const getERC20TokenBalance = async (publicClient: PublicClient, tokenAddress: Address, ownerAddress: Address) => {
21+
const result = await publicClient.readContract({
22+
address: tokenAddress,
23+
abi: [
24+
{
25+
inputs: [{ name: 'owner', type: 'address' }],
26+
name: 'balanceOf',
27+
outputs: [{ name: '', type: 'uint256' }],
28+
stateMutability: 'view',
29+
type: 'function',
30+
}
31+
],
32+
functionName: 'balanceOf',
33+
args: [ownerAddress]
34+
})
35+
36+
return result as bigint
37+
}
38+
39+
const getL1ERC20TokenBalance = async (ownerAddress: Address) => {
40+
return getERC20TokenBalance(l1PublicClient, ECO_L1_TOKEN_ADDRESS, ownerAddress)
41+
}
42+
43+
const getL2ERC20TokenBalance = async (ownerAddress: Address) => {
44+
return getERC20TokenBalance(l2PublicClient, ECO_L2_TOKEN_ADDRESS, ownerAddress)
45+
}
46+
47+
describe('ECO token', () => {
48+
it('sdk should be able to deposit to l1 bridge contract correctly', async () => {
49+
// this code is a bit whack because of the mix of viem + ethers
50+
// TODO: use anviljs, and use viem entirely once the sdk supports it
51+
await l1TestClient.impersonateAccount({ address: ECO_WHALE })
52+
const l1EcoWhaleSigner = await l1Provider.getSigner(ECO_WHALE);
53+
54+
const preBridgeL1EcoWhaleBalance = await getL1ERC20TokenBalance(ECO_WHALE)
55+
56+
const crossChainMessenger = new CrossChainMessenger({
57+
l1SignerOrProvider: l1EcoWhaleSigner,
58+
l2SignerOrProvider: l2Provider,
59+
l1ChainId: 5,
60+
l2ChainId: 420,
61+
// bedrock: true,
62+
bridges: BRIDGE_ADAPTER_DATA[L2ChainID.OPTIMISM_GOERLI]
63+
})
64+
65+
await crossChainMessenger.approveERC20(
66+
ECO_L1_TOKEN_ADDRESS,
67+
ECO_L2_TOKEN_ADDRESS,
68+
ethers.utils.parseEther('0.1'),
69+
)
70+
71+
const txResponse = await crossChainMessenger.depositERC20(
72+
ECO_L1_TOKEN_ADDRESS,
73+
ECO_L2_TOKEN_ADDRESS,
74+
ethers.utils.parseEther('0.1'),
75+
)
76+
77+
await txResponse.wait();
78+
79+
const l1EcoWhaleBalance = await getL1ERC20TokenBalance(ECO_WHALE)
80+
expect(l1EcoWhaleBalance).toEqual(preBridgeL1EcoWhaleBalance - parseEther('0.1'))
81+
}, 20_000)
82+
83+
it('sdk should be able to withdraw into the l2 bridge contract correctly', async () => {
84+
await l2TestClient.impersonateAccount({ address: ECO_WHALE })
85+
const l2EcoWhaleSigner = await l2Provider.getSigner(ECO_WHALE);
86+
87+
const preBridgeL2EcoWhaleBalance = await getL2ERC20TokenBalance(ECO_WHALE)
88+
89+
const crossChainMessenger = new CrossChainMessenger({
90+
l1SignerOrProvider: l1Provider,
91+
l2SignerOrProvider: l2EcoWhaleSigner,
92+
l1ChainId: 5,
93+
l2ChainId: 420,
94+
// bedrock: true,
95+
bridges: BRIDGE_ADAPTER_DATA[L2ChainID.OPTIMISM_GOERLI]
96+
})
97+
98+
const txResponse = await crossChainMessenger.withdrawERC20(
99+
ECO_L1_TOKEN_ADDRESS,
100+
ECO_L2_TOKEN_ADDRESS,
101+
ethers.utils.parseEther('0.1'),
102+
)
103+
104+
await txResponse.wait();
105+
106+
const l2EcoWhaleBalance = await getL2ERC20TokenBalance(ECO_WHALE)
107+
expect(l2EcoWhaleBalance).toEqual(preBridgeL2EcoWhaleBalance - parseEther('0.1'))
108+
}, 20_000)
109+
110+
})

packages/sdk/test-next/proveMessage.spec.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
33
import { z } from 'zod'
44

55
import { CrossChainMessenger } from '../src'
6+
import { l1Provider, l2Provider } from './testUtils/ethersProviders'
67

78
/**
89
* This test repros the bug where legacy withdrawals are not provable
@@ -48,33 +49,12 @@ transactionHash 0xd66fda632b51a8b25a9d260d70da8be57b9930c461637086152633
4849
transactionIndex 0
4950
type
5051
*/
51-
const E2E_RPC_URL_L1 = z
52-
.string()
53-
.url()
54-
.describe('L1 ethereum rpc Url')
55-
.parse(import.meta.env.VITE_E2E_RPC_URL_L1)
56-
const E2E_RPC_URL_L2 = z
57-
.string()
58-
.url()
59-
.describe('L1 ethereum rpc Url')
60-
.parse(import.meta.env.VITE_E2E_RPC_URL_L2)
52+
6153
const E2E_PRIVATE_KEY = z
6254
.string()
6355
.describe('Private key')
6456
.parse(import.meta.env.VITE_E2E_PRIVATE_KEY)
6557

66-
const jsonRpcHeaders = { 'User-Agent': 'eth-optimism/@gateway/backend' }
67-
/**
68-
* Initialize the signer, prover, and cross chain messenger
69-
*/
70-
const l1Provider = new ethers.providers.JsonRpcProvider({
71-
url: E2E_RPC_URL_L1,
72-
headers: jsonRpcHeaders,
73-
})
74-
const l2Provider = new ethers.providers.JsonRpcProvider({
75-
url: E2E_RPC_URL_L2,
76-
headers: jsonRpcHeaders,
77-
})
7858
const l1Wallet = new ethers.Wallet(E2E_PRIVATE_KEY, l1Provider)
7959
const crossChainMessenger = new CrossChainMessenger({
8060
l1SignerOrProvider: l1Wallet,
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import ethers from 'ethers'
2+
import { z } from 'zod'
3+
4+
const E2E_RPC_URL_L1 = z
5+
.string()
6+
.url()
7+
.describe('L1 ethereum rpc Url')
8+
.parse(import.meta.env.VITE_E2E_RPC_URL_L1)
9+
const E2E_RPC_URL_L2 = z
10+
.string()
11+
.url()
12+
.describe('L1 ethereum rpc Url')
13+
.parse(import.meta.env.VITE_E2E_RPC_URL_L2)
14+
15+
const jsonRpcHeaders = { 'User-Agent': 'eth-optimism/@gateway/backend' }
16+
/**
17+
* Initialize the signer, prover, and cross chain messenger
18+
*/
19+
const l1Provider = new ethers.providers.JsonRpcProvider({
20+
url: E2E_RPC_URL_L1,
21+
headers: jsonRpcHeaders,
22+
})
23+
const l2Provider = new ethers.providers.JsonRpcProvider({
24+
url: E2E_RPC_URL_L2,
25+
headers: jsonRpcHeaders,
26+
})
27+
28+
export {
29+
l1Provider,
30+
l2Provider
31+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
createTestClient,
3+
createPublicClient,
4+
createWalletClient,
5+
http,
6+
} from 'viem'
7+
import { goerli, optimismGoerli } from 'viem/chains'
8+
9+
// TODO: use env vars to determine chain so we can support alternate l1/l2 pairs
10+
const L1_CHAIN = goerli;
11+
const L2_CHAIN = optimismGoerli;
12+
const L1_RPC_URL = 'http://localhost:8545';
13+
const L2_RPC_URL = 'http://localhost:9545'
14+
15+
const l1TestClient = createTestClient({
16+
mode: 'anvil',
17+
chain: L1_CHAIN,
18+
transport: http(L1_RPC_URL),
19+
})
20+
21+
22+
const l2TestClient = createTestClient({
23+
mode: 'anvil',
24+
chain: L2_CHAIN,
25+
transport: http(L2_RPC_URL),
26+
})
27+
28+
29+
const l1PublicClient = createPublicClient({
30+
chain: L1_CHAIN,
31+
transport: http(L1_RPC_URL),
32+
})
33+
34+
35+
const l2PublicClient = createPublicClient({
36+
chain: L2_CHAIN,
37+
transport: http(L2_RPC_URL),
38+
})
39+
40+
const l1WalletClient = createWalletClient({
41+
chain: L1_CHAIN,
42+
transport: http(L1_RPC_URL),
43+
})
44+
45+
const l2WalletClient = createWalletClient({
46+
chain: L2_CHAIN,
47+
transport: http(L2_RPC_URL),
48+
})
49+
50+
export {
51+
l1TestClient,
52+
l2TestClient,
53+
l1PublicClient,
54+
l2PublicClient,
55+
l1WalletClient,
56+
l2WalletClient,
57+
}

packages/sdk/vitest.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineConfig } from 'vitest/config'
2+
3+
// https://vitest.dev/config/ - for docs
4+
export default defineConfig({
5+
test: {
6+
setupFiles: './setupVitest.ts',
7+
include: ['test-next/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
8+
},
9+
})

0 commit comments

Comments
 (0)