@avaprotocol/protocols 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -149,6 +149,85 @@ There's no equivalent multi-protocol + multi-chain + addresses + ABIs + topics p
149
149
 
150
150
  Address verification: link to the canonical source (Etherscan-verified deployment, protocol docs, official address book). PRs that don't cite a source for the addresses won't merge.
151
151
 
152
+ ## Tokens sidecar (for non-TS consumers)
153
+
154
+ `yarn build` writes a per-chain JSON file under `dist/tokens/` alongside the JS + d.ts outputs:
155
+
156
+ ```
157
+ dist/tokens/
158
+ ├── ethereum.json (chain 1)
159
+ ├── sepolia.json (chain 11155111)
160
+ ├── base.json (chain 8453)
161
+ ├── base-sepolia.json (chain 84532)
162
+ ├── bnb-mainnet.json (chain 56)
163
+ └── holesky.json (chain 17000)
164
+ ```
165
+
166
+ Each file is a stable-sorted array of `{ id, name, symbol, decimals }` entries — the same schema the EigenLayer-AVS Go aggregator already consumes under `token_whitelist/*.json`, so a Go service can pick up the catalog without depending on the TS toolchain. `id` is the lowercased address (matching Go's read-side normalization). Metadata fields TS consumers use (`description`, `website`, `logoUrl`, `links`) are intentionally omitted; TS callers import the `Tokens` namespace from source instead.
167
+
168
+ ### When to run it
169
+
170
+ - **Automatically** — `yarn build` invokes `yarn build:tokens-sidecar` as its last step, so any release publishes a fresh sidecar in the npm tarball.
171
+ - **Standalone** — `yarn build:tokens-sidecar` when you've edited `src/tokens/` and want to inspect the generated JSON without rebuilding declarations + JS.
172
+
173
+ ### Adding a new chain
174
+
175
+ The script throws fast if the catalog grows a token on a chain that isn't registered in `CHAIN_FILE_NAMES` inside `scripts/build-tokens-sidecar.ts`:
176
+
177
+ ```
178
+ [tokens-sidecar] no file name registered for chain <id>.
179
+ Add it to CHAIN_FILE_NAMES in scripts/build-tokens-sidecar.ts.
180
+ ```
181
+
182
+ Fix is one line — append `[Chains.NewChain]: "newchain-mainnet"` to the map. The error message points at the exact file so this can't silently ship an incomplete sidecar.
183
+
184
+ ### Consuming the sidecar (Go example)
185
+
186
+ ```go
187
+ // EigenLayer-AVS already does this for its checked-in
188
+ // token_whitelist/*.json; the dist/tokens/*.json from this package
189
+ // match the same schema.
190
+ type tokenEntry struct {
191
+ ID string `json:"id"`
192
+ Name string `json:"name"`
193
+ Symbol string `json:"symbol"`
194
+ Decimals int `json:"decimals"`
195
+ }
196
+
197
+ raw, err := os.ReadFile("path/to/dist/tokens/sepolia.json")
198
+ // ... json.Unmarshal(raw, &entries) ...
199
+ ```
200
+
201
+ ## Releasing
202
+
203
+ This package uses [changesets](https://github.com/changesets/changesets) for versioning. The day-to-day flow:
204
+
205
+ ```bash
206
+ # 1. Make your code changes on a feature branch.
207
+ yarn changeset # interactive prompt; pick bump level + write the changelog entry
208
+ git add .changeset && git commit -m "..."
209
+ git push # open PR as normal
210
+ ```
211
+
212
+ When the PR merges to `main`, the `Release` workflow (`.github/workflows/release.yml`) either:
213
+
214
+ - Opens (or updates) a "Version Packages" PR that runs `yarn changeset version` — bumps `package.json` + writes `CHANGELOG.md`. Review and merge that PR.
215
+ - Or, if there are no pending changesets, runs `yarn release` (`yarn build && yarn changeset publish`) — which publishes to npm with the right dist-tag automatically (stable → `latest`, pre-release → its identifier).
216
+
217
+ ### Publishing manually (escape hatch)
218
+
219
+ If you need to publish without the GitHub Action — e.g. an emergency release from a machine that has `npm login` credentials:
220
+
221
+ ```bash
222
+ yarn build # produces dist/ + tokens sidecar
223
+ npm whoami # verify logged in
224
+ npm publish --access public # for stable versions; goes to `latest`
225
+ # or for a pre-release:
226
+ npm publish --access public --tag dev
227
+ ```
228
+
229
+ `prepublishOnly: yarn build` runs the build chain automatically if you forget, so even `npm publish` alone is safe.
230
+
152
231
  ## Versioning
153
232
 
154
233
  Semver. Until `1.0.0`, breaking changes can land in minor versions (`0.x`), but address corrections are always patches.
package/dist/index.cjs CHANGED
@@ -22,6 +22,7 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Chains: () => Chains,
24
24
  Protocols: () => Protocols,
25
+ Tokens: () => Tokens,
25
26
  aaveV3: () => aaveV3,
26
27
  aerodrome: () => aerodrome,
27
28
  aggregatorV3Abi: () => aggregatorV3Abi,
@@ -32,6 +33,7 @@ __export(index_exports, {
32
33
  ethena: () => ethena,
33
34
  fraxEther: () => fraxEther,
34
35
  lido: () => lido,
36
+ lookupToken: () => lookupToken,
35
37
  morphoBlue: () => morphoBlue,
36
38
  rocketPool: () => rocketPool,
37
39
  sky: () => sky,
@@ -844,10 +846,176 @@ var Protocols = Object.freeze({
844
846
  uniswapV3,
845
847
  wrapped
846
848
  });
849
+
850
+ // src/tokens/index.ts
851
+ var USDC = {
852
+ [Chains.EthereumMainnet]: {
853
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
854
+ decimals: 6,
855
+ name: "USD Coin",
856
+ description: "Fully-reserved USD stablecoin issued by Circle.",
857
+ website: "https://www.circle.com/usdc",
858
+ explorer: "https://etherscan.io/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
859
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/USDC.png",
860
+ links: {
861
+ coingecko: "https://www.coingecko.com/en/coins/usd-coin",
862
+ coinmarketcap: "https://coinmarketcap.com/currencies/usd-coin/"
863
+ }
864
+ },
865
+ [Chains.BaseMainnet]: {
866
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
867
+ decimals: 6,
868
+ name: "USD Coin",
869
+ explorer: "https://basescan.org/token/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
870
+ },
871
+ [Chains.Sepolia]: {
872
+ address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
873
+ decimals: 6,
874
+ name: "USD Coin (Sepolia)",
875
+ explorer: "https://sepolia.etherscan.io/token/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
876
+ },
877
+ [Chains.BaseSepolia]: {
878
+ address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
879
+ decimals: 6,
880
+ name: "USD Coin (Base Sepolia)",
881
+ explorer: "https://sepolia.basescan.org/token/0x036CbD53842c5426634e7929541eC2318f3dCF7e"
882
+ },
883
+ [Chains.BnbMainnet]: {
884
+ address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
885
+ decimals: 18,
886
+ name: "Binance-Peg USD Coin",
887
+ explorer: "https://bscscan.com/token/0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"
888
+ }
889
+ };
890
+ var USDT = {
891
+ [Chains.EthereumMainnet]: {
892
+ address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
893
+ decimals: 6,
894
+ name: "Tether USD",
895
+ description: "USD-pegged stablecoin issued by Tether.",
896
+ website: "https://tether.to",
897
+ explorer: "https://etherscan.io/token/0xdAC17F958D2ee523a2206206994597C13D831ec7",
898
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/USDT.png",
899
+ links: {
900
+ coingecko: "https://www.coingecko.com/en/coins/tether",
901
+ coinmarketcap: "https://coinmarketcap.com/currencies/tether/"
902
+ }
903
+ },
904
+ [Chains.BnbMainnet]: {
905
+ address: "0x55d398326f99059fF775485246999027B3197955",
906
+ decimals: 18,
907
+ name: "Binance-Peg Tether USD",
908
+ explorer: "https://bscscan.com/token/0x55d398326f99059fF775485246999027B3197955"
909
+ }
910
+ };
911
+ var WETH = {
912
+ [Chains.EthereumMainnet]: {
913
+ address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
914
+ decimals: 18,
915
+ name: "Wrapped Ether",
916
+ description: "ERC-20 wrapper around native ETH, 1:1 backed.",
917
+ explorer: "https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
918
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/WETH.png",
919
+ links: {
920
+ coingecko: "https://www.coingecko.com/en/coins/weth"
921
+ }
922
+ },
923
+ [Chains.Sepolia]: {
924
+ address: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
925
+ decimals: 18,
926
+ name: "Wrapped Ether (Sepolia)",
927
+ explorer: "https://sepolia.etherscan.io/token/0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
928
+ },
929
+ [Chains.BaseMainnet]: {
930
+ address: "0x4200000000000000000000000000000000000006",
931
+ decimals: 18,
932
+ name: "Wrapped Ether",
933
+ explorer: "https://basescan.org/token/0x4200000000000000000000000000000000000006"
934
+ },
935
+ [Chains.BaseSepolia]: {
936
+ address: "0x4200000000000000000000000000000000000006",
937
+ decimals: 18,
938
+ name: "Wrapped Ether (Base Sepolia)",
939
+ explorer: "https://sepolia.basescan.org/token/0x4200000000000000000000000000000000000006"
940
+ }
941
+ };
942
+ var DAI = {
943
+ [Chains.EthereumMainnet]: {
944
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
945
+ decimals: 18,
946
+ name: "Dai Stablecoin",
947
+ description: "Decentralized USD-pegged stablecoin issued by MakerDAO.",
948
+ website: "https://makerdao.com",
949
+ explorer: "https://etherscan.io/token/0x6B175474E89094C44Da98b954EedeAC495271d0F",
950
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/DAI.png",
951
+ links: {
952
+ coingecko: "https://www.coingecko.com/en/coins/dai",
953
+ coinmarketcap: "https://coinmarketcap.com/currencies/multi-collateral-dai/"
954
+ }
955
+ },
956
+ [Chains.BaseMainnet]: {
957
+ address: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
958
+ decimals: 18,
959
+ name: "Dai Stablecoin",
960
+ explorer: "https://basescan.org/token/0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
961
+ }
962
+ };
963
+ var LINK = {
964
+ [Chains.EthereumMainnet]: {
965
+ address: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
966
+ decimals: 18,
967
+ name: "ChainLink Token",
968
+ description: "Native token of the Chainlink decentralized oracle network.",
969
+ website: "https://chain.link",
970
+ explorer: "https://etherscan.io/token/0x514910771AF9Ca656af840dff83E8264EcF986CA",
971
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/LINK.png",
972
+ links: {
973
+ coingecko: "https://www.coingecko.com/en/coins/chainlink",
974
+ coinmarketcap: "https://coinmarketcap.com/currencies/chainlink/"
975
+ }
976
+ },
977
+ [Chains.Sepolia]: {
978
+ // AAVE V3 faucet-mintable LINK on Sepolia — NOT canonical Chainlink LINK.
979
+ // Templates targeting the AAVE Sepolia market need this address; the real
980
+ // Chainlink LINK on Sepolia is at 0x779877A7B0D9E8603169DdbD7836e478b4624789.
981
+ address: "0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5",
982
+ decimals: 18,
983
+ name: "ChainLink Token (AAVE Sepolia faucet)",
984
+ explorer: "https://sepolia.etherscan.io/token/0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5"
985
+ }
986
+ };
987
+ var Tokens = Object.freeze({
988
+ USDC,
989
+ USDT,
990
+ WETH,
991
+ DAI,
992
+ LINK
993
+ });
994
+ function lookupToken(chainId, address) {
995
+ if (!address) return void 0;
996
+ const target = address.toLowerCase();
997
+ if (chainId) {
998
+ for (const [symbol, byChain] of Object.entries(Tokens)) {
999
+ const entry = byChain[chainId];
1000
+ if (entry && entry.address.toLowerCase() === target) {
1001
+ return { symbol, ...entry };
1002
+ }
1003
+ }
1004
+ }
1005
+ for (const [symbol, byChain] of Object.entries(Tokens)) {
1006
+ for (const entry of Object.values(byChain)) {
1007
+ if (entry && entry.address.toLowerCase() === target) {
1008
+ return { symbol, ...entry };
1009
+ }
1010
+ }
1011
+ }
1012
+ return void 0;
1013
+ }
847
1014
  // Annotate the CommonJS export names for ESM import in node:
848
1015
  0 && (module.exports = {
849
1016
  Chains,
850
1017
  Protocols,
1018
+ Tokens,
851
1019
  aaveV3,
852
1020
  aerodrome,
853
1021
  aggregatorV3Abi,
@@ -858,6 +1026,7 @@ var Protocols = Object.freeze({
858
1026
  ethena,
859
1027
  fraxEther,
860
1028
  lido,
1029
+ lookupToken,
861
1030
  morphoBlue,
862
1031
  rocketPool,
863
1032
  sky,
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./protocols";
2
2
  export { Chains, type ChainId } from "./chains";
3
+ export { Tokens, lookupToken } from "./tokens";
4
+ export type { TokenByChain, TokenChainEntry, TokenLinks } from "./tokens";
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -800,9 +800,175 @@ var Protocols = Object.freeze({
800
800
  uniswapV3,
801
801
  wrapped
802
802
  });
803
+
804
+ // src/tokens/index.ts
805
+ var USDC = {
806
+ [Chains.EthereumMainnet]: {
807
+ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
808
+ decimals: 6,
809
+ name: "USD Coin",
810
+ description: "Fully-reserved USD stablecoin issued by Circle.",
811
+ website: "https://www.circle.com/usdc",
812
+ explorer: "https://etherscan.io/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
813
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/USDC.png",
814
+ links: {
815
+ coingecko: "https://www.coingecko.com/en/coins/usd-coin",
816
+ coinmarketcap: "https://coinmarketcap.com/currencies/usd-coin/"
817
+ }
818
+ },
819
+ [Chains.BaseMainnet]: {
820
+ address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
821
+ decimals: 6,
822
+ name: "USD Coin",
823
+ explorer: "https://basescan.org/token/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
824
+ },
825
+ [Chains.Sepolia]: {
826
+ address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
827
+ decimals: 6,
828
+ name: "USD Coin (Sepolia)",
829
+ explorer: "https://sepolia.etherscan.io/token/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
830
+ },
831
+ [Chains.BaseSepolia]: {
832
+ address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
833
+ decimals: 6,
834
+ name: "USD Coin (Base Sepolia)",
835
+ explorer: "https://sepolia.basescan.org/token/0x036CbD53842c5426634e7929541eC2318f3dCF7e"
836
+ },
837
+ [Chains.BnbMainnet]: {
838
+ address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
839
+ decimals: 18,
840
+ name: "Binance-Peg USD Coin",
841
+ explorer: "https://bscscan.com/token/0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"
842
+ }
843
+ };
844
+ var USDT = {
845
+ [Chains.EthereumMainnet]: {
846
+ address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
847
+ decimals: 6,
848
+ name: "Tether USD",
849
+ description: "USD-pegged stablecoin issued by Tether.",
850
+ website: "https://tether.to",
851
+ explorer: "https://etherscan.io/token/0xdAC17F958D2ee523a2206206994597C13D831ec7",
852
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/USDT.png",
853
+ links: {
854
+ coingecko: "https://www.coingecko.com/en/coins/tether",
855
+ coinmarketcap: "https://coinmarketcap.com/currencies/tether/"
856
+ }
857
+ },
858
+ [Chains.BnbMainnet]: {
859
+ address: "0x55d398326f99059fF775485246999027B3197955",
860
+ decimals: 18,
861
+ name: "Binance-Peg Tether USD",
862
+ explorer: "https://bscscan.com/token/0x55d398326f99059fF775485246999027B3197955"
863
+ }
864
+ };
865
+ var WETH = {
866
+ [Chains.EthereumMainnet]: {
867
+ address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
868
+ decimals: 18,
869
+ name: "Wrapped Ether",
870
+ description: "ERC-20 wrapper around native ETH, 1:1 backed.",
871
+ explorer: "https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
872
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/WETH.png",
873
+ links: {
874
+ coingecko: "https://www.coingecko.com/en/coins/weth"
875
+ }
876
+ },
877
+ [Chains.Sepolia]: {
878
+ address: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
879
+ decimals: 18,
880
+ name: "Wrapped Ether (Sepolia)",
881
+ explorer: "https://sepolia.etherscan.io/token/0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
882
+ },
883
+ [Chains.BaseMainnet]: {
884
+ address: "0x4200000000000000000000000000000000000006",
885
+ decimals: 18,
886
+ name: "Wrapped Ether",
887
+ explorer: "https://basescan.org/token/0x4200000000000000000000000000000000000006"
888
+ },
889
+ [Chains.BaseSepolia]: {
890
+ address: "0x4200000000000000000000000000000000000006",
891
+ decimals: 18,
892
+ name: "Wrapped Ether (Base Sepolia)",
893
+ explorer: "https://sepolia.basescan.org/token/0x4200000000000000000000000000000000000006"
894
+ }
895
+ };
896
+ var DAI = {
897
+ [Chains.EthereumMainnet]: {
898
+ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F",
899
+ decimals: 18,
900
+ name: "Dai Stablecoin",
901
+ description: "Decentralized USD-pegged stablecoin issued by MakerDAO.",
902
+ website: "https://makerdao.com",
903
+ explorer: "https://etherscan.io/token/0x6B175474E89094C44Da98b954EedeAC495271d0F",
904
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/DAI.png",
905
+ links: {
906
+ coingecko: "https://www.coingecko.com/en/coins/dai",
907
+ coinmarketcap: "https://coinmarketcap.com/currencies/multi-collateral-dai/"
908
+ }
909
+ },
910
+ [Chains.BaseMainnet]: {
911
+ address: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
912
+ decimals: 18,
913
+ name: "Dai Stablecoin",
914
+ explorer: "https://basescan.org/token/0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
915
+ }
916
+ };
917
+ var LINK = {
918
+ [Chains.EthereumMainnet]: {
919
+ address: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
920
+ decimals: 18,
921
+ name: "ChainLink Token",
922
+ description: "Native token of the Chainlink decentralized oracle network.",
923
+ website: "https://chain.link",
924
+ explorer: "https://etherscan.io/token/0x514910771AF9Ca656af840dff83E8264EcF986CA",
925
+ logoUrl: "https://raw.githubusercontent.com/AvaProtocol/protocols/main/logos/ethereum/LINK.png",
926
+ links: {
927
+ coingecko: "https://www.coingecko.com/en/coins/chainlink",
928
+ coinmarketcap: "https://coinmarketcap.com/currencies/chainlink/"
929
+ }
930
+ },
931
+ [Chains.Sepolia]: {
932
+ // AAVE V3 faucet-mintable LINK on Sepolia — NOT canonical Chainlink LINK.
933
+ // Templates targeting the AAVE Sepolia market need this address; the real
934
+ // Chainlink LINK on Sepolia is at 0x779877A7B0D9E8603169DdbD7836e478b4624789.
935
+ address: "0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5",
936
+ decimals: 18,
937
+ name: "ChainLink Token (AAVE Sepolia faucet)",
938
+ explorer: "https://sepolia.etherscan.io/token/0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5"
939
+ }
940
+ };
941
+ var Tokens = Object.freeze({
942
+ USDC,
943
+ USDT,
944
+ WETH,
945
+ DAI,
946
+ LINK
947
+ });
948
+ function lookupToken(chainId, address) {
949
+ if (!address) return void 0;
950
+ const target = address.toLowerCase();
951
+ if (chainId) {
952
+ for (const [symbol, byChain] of Object.entries(Tokens)) {
953
+ const entry = byChain[chainId];
954
+ if (entry && entry.address.toLowerCase() === target) {
955
+ return { symbol, ...entry };
956
+ }
957
+ }
958
+ }
959
+ for (const [symbol, byChain] of Object.entries(Tokens)) {
960
+ for (const entry of Object.values(byChain)) {
961
+ if (entry && entry.address.toLowerCase() === target) {
962
+ return { symbol, ...entry };
963
+ }
964
+ }
965
+ }
966
+ return void 0;
967
+ }
803
968
  export {
804
969
  Chains,
805
970
  Protocols,
971
+ Tokens,
806
972
  aaveV3,
807
973
  aerodrome,
808
974
  aggregatorV3Abi,
@@ -813,6 +979,7 @@ export {
813
979
  ethena,
814
980
  fraxEther,
815
981
  lido,
982
+ lookupToken,
816
983
  morphoBlue,
817
984
  rocketPool,
818
985
  sky,
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
4
+ "name": "USD Coin (Base Sepolia)",
5
+ "symbol": "USDC",
6
+ "decimals": 6
7
+ },
8
+ {
9
+ "id": "0x4200000000000000000000000000000000000006",
10
+ "name": "Wrapped Ether (Base Sepolia)",
11
+ "symbol": "WETH",
12
+ "decimals": 18
13
+ }
14
+ ]
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "id": "0x50c5725949a6f0c72e6c4a641f24049a917db0cb",
4
+ "name": "Dai Stablecoin",
5
+ "symbol": "DAI",
6
+ "decimals": 18
7
+ },
8
+ {
9
+ "id": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
10
+ "name": "USD Coin",
11
+ "symbol": "USDC",
12
+ "decimals": 6
13
+ },
14
+ {
15
+ "id": "0x4200000000000000000000000000000000000006",
16
+ "name": "Wrapped Ether",
17
+ "symbol": "WETH",
18
+ "decimals": 18
19
+ }
20
+ ]
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
4
+ "name": "Binance-Peg USD Coin",
5
+ "symbol": "USDC",
6
+ "decimals": 18
7
+ },
8
+ {
9
+ "id": "0x55d398326f99059ff775485246999027b3197955",
10
+ "name": "Binance-Peg Tether USD",
11
+ "symbol": "USDT",
12
+ "decimals": 18
13
+ }
14
+ ]
@@ -0,0 +1,32 @@
1
+ [
2
+ {
3
+ "id": "0x6b175474e89094c44da98b954eedeac495271d0f",
4
+ "name": "Dai Stablecoin",
5
+ "symbol": "DAI",
6
+ "decimals": 18
7
+ },
8
+ {
9
+ "id": "0x514910771af9ca656af840dff83e8264ecf986ca",
10
+ "name": "ChainLink Token",
11
+ "symbol": "LINK",
12
+ "decimals": 18
13
+ },
14
+ {
15
+ "id": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
16
+ "name": "USD Coin",
17
+ "symbol": "USDC",
18
+ "decimals": 6
19
+ },
20
+ {
21
+ "id": "0xdac17f958d2ee523a2206206994597c13d831ec7",
22
+ "name": "Tether USD",
23
+ "symbol": "USDT",
24
+ "decimals": 6
25
+ },
26
+ {
27
+ "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
28
+ "name": "Wrapped Ether",
29
+ "symbol": "WETH",
30
+ "decimals": 18
31
+ }
32
+ ]
@@ -0,0 +1,53 @@
1
+ import { type TokenChainEntry } from "./types";
2
+ export type { TokenByChain, TokenChainEntry, TokenLinks } from "./types";
3
+ /**
4
+ * Symbol → per-chain entry. The outer object is frozen at module
5
+ * load, so the symbol set is immutable. The inner per-chain maps
6
+ * (e.g. `Tokens.USDC`) are NOT deep-frozen — callers must treat
7
+ * them as logically read-only. The shallow freeze is the right
8
+ * tradeoff: deep-freezing the chain maps + their entries on every
9
+ * import would add overhead for a guarantee no consumer has asked
10
+ * for. Add a deep-freeze pass here if a real misuse case emerges.
11
+ *
12
+ * Symbol keys are the canonical uppercase ERC-20 ticker (USDC, not
13
+ * usdc/Usdc). The catalog is intentionally not case-insensitive at
14
+ * this level — callers needing a fuzzy lookup should normalize at
15
+ * their own boundary.
16
+ */
17
+ export declare const Tokens: Readonly<{
18
+ USDC: Partial<Record<number, TokenChainEntry>>;
19
+ USDT: Partial<Record<number, TokenChainEntry>>;
20
+ WETH: Partial<Record<number, TokenChainEntry>>;
21
+ DAI: Partial<Record<number, TokenChainEntry>>;
22
+ LINK: Partial<Record<number, TokenChainEntry>>;
23
+ }>;
24
+ /**
25
+ * Reverse lookup: given a contract address, find the token entry plus
26
+ * its symbol. Address matching is case-insensitive so callers can pass
27
+ * checksum or lowercase without normalizing upstream.
28
+ *
29
+ * Resolution strategy:
30
+ * 1. When `chainId` is provided, prefer a match registered for that
31
+ * chain.
32
+ * 2. If no chain-specific match (or no chainId), scan every chain
33
+ * for the address. Necessary when the caller's chain context
34
+ * diverges from where the address actually lives — happens in
35
+ * multi-chain dev gateways where the workflow targets one chain
36
+ * but the runtime is bound to a different one. Most ERC-20
37
+ * addresses are globally unique so this collapses to a single
38
+ * match; for OP-stack predeploys (e.g. WETH at 0x4200…0006 on
39
+ * both Base and Base Sepolia), any match yields the correct
40
+ * symbol and decimals.
41
+ *
42
+ * Returns `undefined` when no chain in the catalog carries the
43
+ * address, or when the address is missing/falsy.
44
+ *
45
+ * Typical use: aggregator payloads ship `tokenSymbol="UNKNOWN"` for
46
+ * tokens whose chain-specific RPC enrichment failed; consumers fall
47
+ * through to `lookupToken(chainId, contractAddress)` and recover the
48
+ * symbol + decimals from this catalog.
49
+ */
50
+ export declare function lookupToken(chainId: number | undefined, address: string | undefined): (TokenChainEntry & {
51
+ symbol: string;
52
+ }) | undefined;
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAElE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA+IzE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,MAAM;;;;;;EAMgC,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,CAAC,eAAe,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAsBpD"}
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "id": "0xf8fb3713d459d7c1018bd0a49d19b4c44290ebe5",
4
+ "name": "ChainLink Token (AAVE Sepolia faucet)",
5
+ "symbol": "LINK",
6
+ "decimals": 18
7
+ },
8
+ {
9
+ "id": "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238",
10
+ "name": "USD Coin (Sepolia)",
11
+ "symbol": "USDC",
12
+ "decimals": 6
13
+ },
14
+ {
15
+ "id": "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
16
+ "name": "Wrapped Ether (Sepolia)",
17
+ "symbol": "WETH",
18
+ "decimals": 18
19
+ }
20
+ ]
@@ -0,0 +1,47 @@
1
+ import { type ChainId } from "../chains";
2
+ /**
3
+ * External reference links for a token. All fields optional — the
4
+ * catalog only records what's actually known per token. Add new keys
5
+ * here when adding a category we want to surface; consumers ignore
6
+ * unknown keys harmlessly.
7
+ */
8
+ export interface TokenLinks {
9
+ readonly github?: string;
10
+ readonly twitter?: string;
11
+ readonly coingecko?: string;
12
+ readonly coinmarketcap?: string;
13
+ readonly reddit?: string;
14
+ readonly blog?: string;
15
+ readonly whitepaper?: string;
16
+ }
17
+ /**
18
+ * Per-chain token deployment record. `address` and `decimals` are
19
+ * required because they're the universal lookup primitives every
20
+ * consumer needs. All other fields are optional metadata.
21
+ *
22
+ * `name` is per-chain because some tokens use different display names
23
+ * on different deployments (e.g. AAVE's faucet-LINK on Sepolia is
24
+ * "ChainLink Token" vs. "Chainlink" on mainnet). When the chains
25
+ * agree, the same string is fine to repeat.
26
+ *
27
+ * `logoUrl` is a fully qualified URL — typically a GitHub Raw or CDN
28
+ * link. Studio renders local PNGs by resolving symbol+chain itself
29
+ * and ignores this field; backend consumers may surface the URL
30
+ * directly in summaries or skip it.
31
+ */
32
+ export interface TokenChainEntry {
33
+ readonly address: `0x${string}`;
34
+ readonly decimals: number;
35
+ readonly name?: string;
36
+ readonly description?: string;
37
+ readonly website?: string;
38
+ readonly explorer?: string;
39
+ readonly logoUrl?: string;
40
+ readonly links?: TokenLinks;
41
+ }
42
+ /**
43
+ * Map of `ChainId → TokenChainEntry`. `Partial` because no token is
44
+ * deployed on every chain in the catalog.
45
+ */
46
+ export type TokenByChain = Partial<Record<ChainId, TokenChainEntry>>;
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tokens/types.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avaprotocol/protocols",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Multi-chain catalog of DeFi protocol contract addresses, ABI fragments, and event topic hashes — covers AAVE V3, Uniswap V3, Chainlink, Lido, Morpho, and more.",
5
5
  "keywords": [
6
6
  "defi",
@@ -50,17 +50,23 @@
50
50
  "scripts": {
51
51
  "build:declarations": "tsc -p tsconfig.build.json",
52
52
  "build:js": "tsup src/index.ts --format cjs,esm --target es2020",
53
- "build": "yarn clean:dist && yarn build:declarations && yarn build:js",
53
+ "build:tokens-sidecar": "tsx scripts/build-tokens-sidecar.ts",
54
+ "build": "yarn clean:dist && yarn build:declarations && yarn build:js && yarn build:tokens-sidecar",
54
55
  "clean": "rm -rf node_modules dist tsconfig.tsbuildinfo",
55
56
  "clean:dist": "rm -rf dist tsconfig.tsbuildinfo",
56
57
  "test": "vitest",
57
58
  "test:run": "vitest run",
58
59
  "typecheck": "tsc --noEmit",
59
60
  "lint": "tsc --noEmit",
60
- "prepublishOnly": "yarn build"
61
+ "prepublishOnly": "yarn build",
62
+ "changeset": "changeset",
63
+ "version-packages": "changeset version",
64
+ "release": "yarn build && changeset publish"
61
65
  },
62
66
  "devDependencies": {
67
+ "@changesets/cli": "^2.31.0",
63
68
  "tsup": "^8.5.0",
69
+ "tsx": "^4.19.0",
64
70
  "typescript": "^5.5.4",
65
71
  "vitest": "^1.6.0"
66
72
  },