Skip to content

Commit 25e2c88

Browse files
authored
Autogenerate explorer links in address lists (#545)
# Description Reviewing the addition or removal of new chains in the contract addresses is always difficult to review. See for example [this PR](#538): there are lines with a thousand characters getting modified and it's very easy to miss differences that could [lead to issues](#543). I tried two ways to work around this. One is better table formatting, but the solution I found (idea from [here](facebook/docusaurus#5155 (reply in thread))) requires relying on some relatively obscure package [remark-grid-tables](https://github.com/zestedesavoir/zmarkdown/tree/master/packages/remark-grid-tables). I didn't want to add an unknown dependency. The other way was by relying on the fact that Docusaurus uses React. This PR is my attempt to use React to make adding chains easier. This PR is my proposal on how to tackle this issue. I've never done any react before so the code is likely low quality and I don't fully understand what I'm doing. Please review carefully and question every choice! Ideas for the future: it would be great to add the chain icon next to the name, so it's easier to find the chain you need. # Changes - Added new function to build links to explorer for a single contract. It has some minimal customization via option parameters. - Used new function in every place I could. (There are still a lot of "raw" Etherscan link around in .md files. I think that's ok, if we feel the need we can migrate them to use this function.) # Known issues Lens explorer doesn't follow the same anchor format used by Etherscan, unlike all other explorers. This means that things like `https://explorer.lens.xyz/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#code` doesn't link to the code, the anchor should be `#contract`. I could add some code to handle this edge case, but for now I'd leave it as it is since it's just Lens. The link still works, it's just that it doesn't point to the code. # How to test Visually compare the preview of this PR with the current version. Click on a few links to confirm they point to the expected URL. List: - Periphery: [current](https://docs.cow.fi/cow-protocol/reference/contracts/periphery), [new](https://docs-git-autogenerate-explorer-links-cowswap.vercel.app/cow-protocol/reference/contracts/periphery). - Core: [current](https://docs.cow.fi/cow-protocol/reference/contracts/core), [new](https://docs-git-autogenerate-explorer-links-cowswap.vercel.app/cow-protocol/reference/contracts/core). - Signing schemes: [current](https://docs.cow.fi/cow-protocol/reference/core/signing-schemes), [new](https://docs-git-autogenerate-explorer-links-cowswap.vercel.app/cow-protocol/reference/core/signing-schemes).
1 parent adacc28 commit 25e2c88

File tree

4 files changed

+126
-55
lines changed

4 files changed

+126
-55
lines changed

docs/cow-protocol/reference/contracts/core/README.mdx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
sidebar_position: 1
33
---
44

5+
import { explorerLinks } from '/src/components/contract-addresses'
6+
57
# Core
68

79
Core contracts of CoW Protocol are those that are necessary for the protocol to function.
@@ -40,11 +42,11 @@ Take care when [signing](../core/signing-schemes), ensuring that the `EIP-712` d
4042

4143
:::
4244

43-
| **Contracts** | **Address / Chains** |
44-
|---|---|
45-
| [`GPv2Settlement`](core/settlement)<br />Upgradeable: No ❎<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol) | `0x9008D19f58AAbD9eD0D60971565AA8510560ab41`<br />- [Ethereum mainnet](https://etherscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br />- [Gnosis chain](https://gnosisscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Arbitrum one](https://arbiscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Base](https://basescan.org/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Avalanche](https://snowscan.xyz/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Polygon](https://polygonscan.com/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Lens](https://explorer.lens.xyz/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#contract)<br/>- [BNB](https://bscscan.com/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code)<br/>- [Sepolia](https://sepolia.etherscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41/#code) |
46-
| [`GPv2AllowListAuthentication`](core/allowlist)<br />Upgradeable: Yes ⚠️<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2AllowListAuthentication.sol) | `0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE`<br />- [Ethereum mainnet](https://etherscan.io/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br />- [Gnosis chain](https://gnosisscan.io/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Arbitrum one](https://arbiscan.io/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Base](https://basescan.org/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Avalanche](https://snowscan.xyz/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Polygon](https://polygonscan.com/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Lens](https://explorer.lens.xyz/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#contract)<br/>- [BNB](https://bscscan.com/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code)<br/>- [Sepolia](https://sepolia.etherscan.io/address/0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE/#code) |
47-
| [`GPv2VaultRelayer`](core/vault-relayer)<br />Upgradeable: No ❎<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2VaultRelayer.sol) | `0xC92E8bdf79f0507f65a392b0ab4667716BFE0110`<br />- [Ethereum mainnet](https://etherscan.io/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br />- [Gnosis chain](https://gnosisscan.io/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Arbitrum one](https://arbiscan.io/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Base](https://basescan.org/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Avalanche](https://snowscan.xyz/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Polygon](https://polygonscan.com/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Lens](https://explorer.lens.xyz/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110#contract)<br/>- [BNB](https://bscscan.com/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code)<br/>- [Sepolia](https://sepolia.etherscan.io/address/0xC92E8bdf79f0507f65a392b0ab4667716BFE0110/#code) |
45+
| **Contracts** | **Address / Chains** |
46+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
47+
| [`GPv2Settlement`](core/settlement)<br />Upgradeable: No ❎<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol) | `0x9008D19f58AAbD9eD0D60971565AA8510560ab41`<br />{explorerLinks(["Ethereum","Gnosis","Arbitrum One","Base","Avalanche","Polygon","Lens","BNB","Sepolia"], "0x9008D19f58AAbD9eD0D60971565AA8510560ab41", {separator: <br />})} |
48+
| [`GPv2AllowListAuthentication`](core/allowlist)<br />Upgradeable: Yes ⚠️<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2AllowListAuthentication.sol) | `0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE`<br />{explorerLinks(["Ethereum","Gnosis","Arbitrum One","Base","Avalanche","Polygon","Lens","BNB","Sepolia"], "0x2c4c28DDBdAc9C5E7055b4C863b72eA0149D8aFE", {separator: <br />})} |
49+
| [`GPv2VaultRelayer`](core/vault-relayer)<br />Upgradeable: No ❎<br />[GitHub](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2VaultRelayer.sol) | `0xC92E8bdf79f0507f65a392b0ab4667716BFE0110`<br />{explorerLinks(["Ethereum","Gnosis","Arbitrum One","Base","Avalanche","Polygon","Lens","BNB","Sepolia"], "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110", {separator: <br />})} |
4850

4951
<details close>
5052
<summary>Upgradeable allow-list</summary>
Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { explorerLinks } from '/src/components/contract-addresses'
2+
13
# Periphery
24

35
Peripheral contracts are those that are not necessary for CoW Protocol to function, but are used to enhance user experience.
46

5-
67
## Deployments
78

89
### EthFlow
@@ -11,12 +12,12 @@ Peripheral contracts are those that are not necessary for CoW Protocol to functi
1112
**Upgradeable**: No ❎
1213
**GitHub**: [CoWSwapEthFlow.sol](https://github.com/cowprotocol/ethflowcontract/blob/main/src/CoWSwapEthFlow.sol)
1314

14-
| Environment | Address | Networks |
15-
|--------------------------|----------------------------------------------|----------|
16-
| Production (most chains) | `0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC` | [Mainnet](https://etherscan.io/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Gnosis chain](https://gnosisscan.io/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Arbitrum one](https://arbiscan.io/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Base](https://basescan.org/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Avalanche](https://snowscan.xyz/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Polygon](https://polygonscan.com/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [BNB](https://bscscan.com/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), [Sepolia](https://sepolia.etherscan.io/address/0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC), but not Lens! |
17-
| Staging (most chains) | `0x04501b9b1D52e67f6862d157E00D13419D2D6E95` | [Mainnet](https://etherscan.io/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Gnosis chain](https://gnosisscan.io/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Arbitrum one](https://arbiscan.io/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Base](https://basescan.org/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Avalanche](https://snowscan.xyz/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Polygon](https://polygonscan.com/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [BNB](https://bscscan.com/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), [Sepolia](https://sepolia.etherscan.io/address/0x04501b9b1D52e67f6862d157E00D13419D2D6E95), but not Lens! |
18-
| Production (Lens) | `0x5A5b8aE7a0b4C0EAf453d10DCcfbA413f07ebdC2` | [Lens](https://explorer.lens.xyz/address/0x5A5b8aE7a0b4C0EAf453d10DCcfbA413f07ebdC2) only!
19-
| Staging (Lens) | `0xFb337f8a725A142f65fb9ff4902d41cc901de222` | [Lens](https://explorer.lens.xyz/address/0xFb337f8a725A142f65fb9ff4902d41cc901de222) only!
15+
| Environment | Address | Networks |
16+
| ------------------------ | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17+
| Production (most chains) | `0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC` | {explorerLinks(["Ethereum","Gnosis","Arbitrum One","Base","Avalanche","Polygon","BNB","Sepolia"], "0xbA3cB449bD2B4ADddBc894D8697F5170800EAdeC")}, but not Lens! |
18+
| Staging (most chains) | `0x04501b9b1D52e67f6862d157E00D13419D2D6E95` | {explorerLinks(["Ethereum","Gnosis","Arbitrum One","Base","Avalanche","Polygon","BNB","Sepolia"], "0x04501b9b1D52e67f6862d157E00D13419D2D6E95")}, but not Lens! |
19+
| Production (Lens) | `0x5A5b8aE7a0b4C0EAf453d10DCcfbA413f07ebdC2` | {explorerLinks("Lens", "0x5A5b8aE7a0b4C0EAf453d10DCcfbA413f07ebdC2")} only! |
20+
| Staging (Lens) | `0xFb337f8a725A142f65fb9ff4902d41cc901de222` | {explorerLinks("Lens", "0xFb337f8a725A142f65fb9ff4902d41cc901de222")} only! |
2021

2122
:::warning
2223

@@ -31,28 +32,28 @@ Be very careful to choose the right address in the chains you're currently using
3132
**Upgradeable**: No ❎
3233
**GitHub**: [HooksTrampoline.sol](https://github.com/cowprotocol/hooks-trampoline/blob/main/src/HooksTrampoline.sol)
3334

34-
**Address**: `0x60bf78233f48ec42ee3f101b9a05ec7878728006`
35-
35+
**Address**: `0x60Bf78233f48eC42eE3F101b9a05eC7878728006`
3636

3737
:::warning
3838

3939
You should not assume that the HooksTrampoline contract address will remain fixed. Each solver may choose to use this implementation or another one, as long as it fulfills the hook intent.
4040

4141
:::
4242

43-
44-
45-
**Networks**:
46-
[Mainnet](https://etherscan.io/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
47-
[Sepolia](https://sepolia.etherscan.io/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
48-
[Gnosis chain](https://gnosisscan.io/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
49-
[Base](https://basescan.org/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
50-
[Arbitrum one](https://arbiscan.io/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
51-
[Avalanche](https://snowscan.xyz/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
52-
[Polygon](https://polygonscan.com/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
53-
[Lens](https://explorer.lens.xyz/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
54-
[BNB](https://bscscan.com/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
55-
[Optimism](https://optimistic.etherscan.io/address/0x60bf78233f48ec42ee3f101b9a05ec7878728006),
43+
**Networks**:
44+
45+
{explorerLinks([
46+
"Ethereum",
47+
"Sepolia",
48+
"Gnosis",
49+
"Base",
50+
"Arbitrum One",
51+
"Avalanche",
52+
"Polygon",
53+
"Lens",
54+
"BNB",
55+
"Optimism"
56+
], "0x60Bf78233f48eC42eE3F101b9a05eC7878728006")}
5657

5758
### ComposableCoW
5859

@@ -62,24 +63,27 @@ You should not assume that the HooksTrampoline contract address will remain fixe
6263

6364
**Address**: `0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74`
6465

65-
**Networks**:
66-
[Mainnet](https://etherscan.io/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
67-
[Gnosis chain](https://gnosisscan.io/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
68-
[Arbitrum one](https://arbiscan.io/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
69-
[Base](https://basescan.org/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
70-
[Avalanche](https://snowscan.xyz/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
71-
[Polygon](https://polygonscan.com/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
72-
[Lens](https://explorer.lens.xyz/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
73-
[BNB](https://bscscan.com/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74),
74-
[Sepolia](https://sepolia.etherscan.io/address/0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74)
66+
**Networks**:
67+
68+
{explorerLinks([
69+
"Ethereum",
70+
"Gnosis",
71+
"Arbitrum One",
72+
"Base",
73+
"Avalanche",
74+
"Polygon",
75+
"Lens",
76+
"BNB",
77+
"Sepolia",
78+
], "0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74")}
7579

7680
### CoWUidGenerator
7781

7882
**Documentation**: [`CoWUidGenerator`](periphery/cow-uid-generator)
7983
**Upgradeable**: No ❎
8084

81-
| Network | Address |
82-
|---------|---------|
83-
| [Mainnet](https://etherscan.io/address/0xe84dcd8587287b997f51299430a396ad03aaec06) | `0xe84DCd8587287B997F51299430A396AD03aAEC06` |
84-
| [Gnosis chain](https://gnosisscan.io/address/0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711) | `0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711` |
85-
| [Base](https://basescan.org/address/0x96dddac514d0799e34e3f642c5006852ad24cd68) | `0x96ddDAC514d0799e34e3F642c5006852aD24CD68` |
85+
| Network | Address |
86+
| ------------------------------------------------------------------------- | -------------------------------------------- |
87+
| {explorerLinks("Ethereum", "0xe84DCd8587287B997F51299430A396AD03aAEC06")} | `0xe84DCd8587287B997F51299430A396AD03aAEC06` |
88+
| {explorerLinks("Gnosis", "0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711")} | `0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711` |
89+
| {explorerLinks("Base", "0x96ddDAC514d0799e34e3F642c5006852aD24CD68")} | `0x96ddDAC514d0799e34e3F642c5006852aD24CD68` |

docs/cow-protocol/reference/core/signing_schemes.mdx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ id: signing-schemes
33
---
44

55
import GPv2Note from '../../../partials/_gpv2.mdx'
6+
import { explorerLinks } from '/src/components/contract-addresses'
67

78
# Signing schemes
89

@@ -35,16 +36,17 @@ The actual domain separator is the result of hashing the previous `EIP712Domain`
3536

3637
For convenience, the domain separator is exposed as a public view function in the settlement contract:
3738

38-
* [Ethereum mainnet](https://etherscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
39-
* [Gnosis chain](https://gnosisscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
40-
* [Arbitrum one](https://arbiscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
41-
* [Base](https://basescan.org/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
42-
* [Avalanche](https://snowscan.xyz/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
43-
* [Polygon](https://polygonscan.com/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
44-
* [Lens](https://explorer.lens.xyz/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#contract#read)
45-
* [BNB](https://bscscan.com/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
46-
* [Sepolia](https://sepolia.etherscan.io/address/0x9008D19f58AAbD9eD0D60971565AA8510560ab41#readContract#F2)
47-
39+
{explorerLinks([
40+
"Ethereum",
41+
"Gnosis",
42+
"Arbitrum One",
43+
"Base",
44+
"Avalanche",
45+
"Polygon",
46+
"Lens",
47+
"BNB",
48+
"Sepolia",
49+
], "0x9008D19f58AAbD9eD0D60971565AA8510560ab41", {separator: <br />, urlTrailing: "#readContract#F2"})}
4850

4951
:::
5052

@@ -122,15 +124,15 @@ console.log(`Order digest: ${digest}`)
122124

123125
For convenience, we also deployed a small helper contract that makes it easy to compute order uids as well as their full encoding for each chain using the Etherscan UI:
124126

125-
- [Mainnet](https://etherscan.io/address/0xe84dcd8587287b997f51299430a396ad03aaec06#readContract#F1)
126-
- [Gnosis Chain](https://gnosisscan.io/address/0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711#readContract#F1)
127+
- {explorerLinks("Ethereum", "0xe84dcd8587287b997f51299430a396ad03aaec06", {urlTrailing: "#readContract#F1"})}
128+
- {explorerLinks("Gnosis", "0xCA51403B524dF7dA6f9D6BFc64895AD833b5d711", {urlTrailing: "#readContract#F1"})}
127129
- Base N/A
128130
- Arbitrum N/A
129131
- Avalanche N/A
130132
- Polygon N/A
131133
- Lens N/A
132134
- BNB N/A
133-
- [Sepolia](https://sepolia.etherscan.io/address/0x59Ffd6c1823F212D49887230f155A35451FdDbfa#readContract#F1)
135+
- {explorerLinks("Sepolia", "0x59Ffd6c1823F212D49887230f155A35451FdDbfa", {urlTrailing: "#readContract#F1"})}
134136

135137
## Supported schemes
136138

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React from 'react';
2+
import Link from '@docusaurus/Link';
3+
4+
export const explorerByChain = {
5+
"Ethereum": "https://etherscan.io",
6+
"Sepolia": "https://sepolia.etherscan.io",
7+
"Gnosis": "https://gnosisscan.io",
8+
"Base": "https://basescan.org",
9+
"Arbitrum One": "https://arbiscan.io",
10+
"Avalanche": "https://snowscan.xyz",
11+
"Polygon": "https://polygonscan.com",
12+
"Lens": "https://explorer.lens.xyz",
13+
"BNB": "https://bscscan.com",
14+
"Optimism": "https://optimistic.etherscan.io",
15+
} as const;
16+
17+
interface ExplorerUrlOptions {
18+
/**
19+
* Which parameters to append after the Explorer URL. For example, #code
20+
* points to the contract code.
21+
*/
22+
urlTrailing?: string | undefined,
23+
}
24+
export function explorerUrl(chain: string, address: string, params?: ExplorerUrlOptions) {
25+
const urlTrailing = params?.urlTrailing ?? "";
26+
if (!(chain in explorerByChain)) {
27+
throw new Error(`Explorer URL for chain ${chain} is not known`);
28+
}
29+
return `${explorerByChain[chain]}/address/${address}/${urlTrailing}`;
30+
}
31+
32+
interface ExplorerLinksOptions {
33+
/**
34+
* Which string or React component to insert between links.
35+
* Defaults to the string ", ".
36+
*/
37+
separator?: React.ReactNode | undefined;
38+
/**
39+
* Which parameters to append after the URL. For example, #code points to the
40+
* contract code.
41+
* Defaults to #code.
42+
*/
43+
urlTrailing?: string | undefined,
44+
}
45+
/**
46+
* Given an array of chains and an address, it returns a component with all the
47+
* chains separated by a comma. All chain names are links to that chain's
48+
* explorer for the given address.
49+
*/
50+
export function explorerLinks(chains: string[] | string, address: string, options?: ExplorerLinksOptions): React.ReactNode {
51+
chains = (typeof chains == "string")? [chains] : chains;
52+
const separator = options?.separator ?? ", ";
53+
const urlTrailing = options?.urlTrailing ?? "#code";
54+
return chains.reduce(
55+
(acc, chain) => {
56+
if (acc.length) {
57+
acc.push(separator)
58+
}
59+
acc.push(<Link to={explorerUrl(chain, address, {urlTrailing})}>{chain}</Link>)
60+
return acc
61+
}
62+
, []);
63+
}

0 commit comments

Comments
 (0)