Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ INFURA_KEY=
MAINNET_RPC="https://mainnet.infura.io/v3/${INFURA_KEY}"
GOERLI_RPC="https://goerli.infura.io/v3/${INFURA_KEY}"
SEPOLIA_RPC="https://sepolia.infura.io/v3/${INFURA_KEY}"
ARB_ONE_RPC="https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}"
ARB_SEPOLIA_RPC="https://arbitrum-sepolia.infura.io/v3/${INFURA_KEY}"
l2NetworkID=42161
PORT=3000
PORT=3000
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"update": "yarn ts-node src/main.ts update",
"arbify": "yarn ts-node src/main.ts arbify",
"permit": "yarn ts-node src/main.ts permit",
"crossChain": "yarn ts-node src/main.ts crosschain --crossChain",
"fullList": "yarn ts-node src/main.ts full --tokenList full --ignorePreviousList",
"allTokensList": "yarn ts-node src/main.ts alltokenslist --tokenList full",
"updateNova": "yarn ts-node src/main.ts update --l2NetworkID 42170",
Expand Down
3 changes: 2 additions & 1 deletion src/PermitTokens/permitSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,10 @@ enum PermitTypes {

export const addPermitTags = async (
tokenList: ArbTokenList,
l2ChainId: number,
): Promise<ArbTokenList> => {
console.log('Adding permit tags');
const { l1, l2 } = await getNetworkConfig();
const { l1, l2 } = await getNetworkConfig(l2ChainId);

const value = utils.parseUnits('1.0', 18);
const deadline = constants.MaxUint256;
Expand Down
17 changes: 13 additions & 4 deletions src/commands/allTokensList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ export const command = Action.AllTokensList;

export const describe = 'All tokens list';

export const handler = async (argvs: Args) => {
let tokenList: ArbTokenList = await generateFullListFormatted();
if (argvs.includePermitTags) tokenList = await addPermitTags(tokenList);
writeToFile(tokenList, argvs.newArbifiedList);
export const handler = async ({
includePermitTags,
l2NetworkID,
newArbifiedList,
}: Args) => {
if (!l2NetworkID) {
throw new Error('l2NetworkID is required');
}
let tokenList: ArbTokenList = await generateFullListFormatted(l2NetworkID);
if (includePermitTags) {
tokenList = await addPermitTags(tokenList, l2NetworkID);
}
writeToFile(tokenList, newArbifiedList);
return tokenList;
};
31 changes: 23 additions & 8 deletions src/commands/arbify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,37 @@ import { addPermitTags } from '../PermitTokens/permitSignature';
import { writeToFile } from '../lib/store';
import { Action, Args } from '../lib/options';
import { arbifyL1List } from '../lib/token_list_gen';
import { getTokenListObj, removeInvalidTokensFromList } from '../lib/utils';

export const command = Action.Arbify;

export const describe = 'Arbify';

export const handler = async (argvs: Args) => {
const includeOldDataFields = !!argvs.includeOldDataFields;
export const handler = async ({
ignorePreviousList,
includeOldDataFields,
includePermitTags,
l2NetworkID,
newArbifiedList,
prevArbifiedList,
tokenList: tokenListPath,
}: Args) => {
if (!l2NetworkID) {
throw new Error('l2NetworkID is required');
}

const { newList } = await arbifyL1List(argvs.tokenList, {
includeOldDataFields,
ignorePreviousList: argvs.ignorePreviousList,
prevArbifiedList: argvs.prevArbifiedList,
const l1TokenList = await getTokenListObj(tokenListPath);
removeInvalidTokensFromList(l1TokenList);
const newList = await arbifyL1List(tokenListPath, l1TokenList, l2NetworkID, {
includeOldDataFields: !!includeOldDataFields,
ignorePreviousList,
prevArbifiedList,
});
let tokenList: ArbTokenList = newList;

if (argvs.includePermitTags) tokenList = await addPermitTags(tokenList);
writeToFile(tokenList, argvs.newArbifiedList);
if (includePermitTags) {
tokenList = await addPermitTags(tokenList, l2NetworkID);
}
writeToFile(tokenList, newArbifiedList);
return tokenList;
};
223 changes: 223 additions & 0 deletions src/commands/crossChain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { ArbTokenInfo, ArbTokenList } from '../lib/types';
import { addPermitTags } from '../PermitTokens/permitSignature';
import { writeToFile } from '../lib/store';
import { Action, Args } from '../lib/options';
import { arbifyL1List } from '../lib/token_list_gen';
import { getTokenListObj, removeInvalidTokensFromList } from '../lib/utils';
import { ChainId } from '../lib/constants';

export const command = Action.CrossChain;

export const describe = 'Cross-chain';

const L2_CHAIN_IDS = [
ChainId.ArbitrumOne,
ChainId.ArbitrumNova,
ChainId.ArbitrumGoerli,
ChainId.ArbitrumSepolia,
];
function isValidL1(chainId: number) {
return [ChainId.Ethereum, ChainId.Goerli, ChainId.Sepolia].includes(chainId);
}
function isValidL2(chainId: number) {
return L2_CHAIN_IDS.includes(chainId);
}
function isValidL3(chainId: number) {
return !isValidL1(chainId) && !isValidL2(chainId);
}
function getL2ParentChain(chainId: number) {
return {
[ChainId.ArbitrumOne]: ChainId.Ethereum,
[ChainId.ArbitrumNova]: ChainId.Ethereum,
[ChainId.ArbitrumSepolia]: ChainId.Sepolia,
[ChainId.ArbitrumGoerli]: ChainId.Goerli,
}[chainId];
}
function getL3ParentChain(chainId: number) {
return {
[ChainId.Xai]: ChainId.ArbitrumOne,
[ChainId.XaiTestnet]: ChainId.ArbitrumGoerli,
[ChainId.Rari]: ChainId.ArbitrumOne,
}[chainId];
}

type TokensMap = Map<string, ArbTokenInfo>;
function getMapKey(chainId: number | string, tokenAddress: string) {
return `${chainId}_${tokenAddress.toLowerCase()}`;
}

// Update token in tokensMap with new bridgeInfo
function updateBridgeInfo(
mapKey: string,
tokensMap: TokensMap,
bridgeInfo: {
[chainId: number]: {
tokenAddress: string;
originBridgeAddress: string;
destBridgeAddress: string;
};
},
) {
const existingToken = tokensMap.get(mapKey);
if (!existingToken) {
console.log('Token not found in TokensMap', mapKey);
return;
}

const newToken: ArbTokenInfo = {
...existingToken,
extensions: {
bridgeInfo: {
...(existingToken.extensions?.bridgeInfo as object),
...bridgeInfo,
},
},
};
tokensMap.set(mapKey, newToken);
return newToken;
}

function updateTokensMap(
tokens: ArbTokenInfo[],
tokensMap: TokensMap,
chainId: number,
) {
// For each L2/L3 tokens, update the parents chain list up to the L1
return tokens.forEach((token) => {
const originChainId = Object.keys(token.extensions?.bridgeInfo || {})[0];

if (!originChainId) {
console.log('No origin chain id found for token', token.address);
return;
}

const bridgeInfo = token.extensions?.bridgeInfo[originChainId];
if (!bridgeInfo) {
console.log('No bridge info found for token', token.address);
return;
}

// Update the direct parent chain's bridgeInfo
const updatedToken = updateBridgeInfo(
getMapKey(originChainId, bridgeInfo.tokenAddress),
tokensMap,
{
[chainId]: {
tokenAddress: token.address,
destBridgeAddress: bridgeInfo.destBridgeAddress,
originBridgeAddress: bridgeInfo.originBridgeAddress,
},
},
);

if (!updatedToken || !isValidL3(chainId)) {
return;
}

const l1ChainId = getL2ParentChain(Number(originChainId));
if (!l1ChainId) {
// This should never happen
throw new Error(`No L1 chain found for L3 token ${token.address}`);
}

const l1Address =
updatedToken.extensions!.bridgeInfo[l1ChainId].tokenAddress;
// Update the L3 bridgeInfo with L1 information
updateBridgeInfo(getMapKey(chainId, token.address), tokensMap, {
[l1ChainId]: {
tokenAddress: l1Address,
destBridgeAddress: 'N/A',
originBridgeAddress: 'N/A',
},
});

// Update the L1 bridgeInfo with L3 information
updateBridgeInfo(getMapKey(l1ChainId, l1Address), tokensMap, {
[chainId]: {
tokenAddress: token.address,
destBridgeAddress: 'N/A',
originBridgeAddress: 'N/A',
},
});
});
}
export const handler = async (argvs: Args) => {
const includeOldDataFields = !!argvs.includeOldDataFields;

const l1TokenList = await getTokenListObj(argvs.tokenList);
removeInvalidTokensFromList(l1TokenList);

// Store all L1 tokens in a map, so we can easily retrieve and update them
const tokensMap = new Map<string, ArbTokenInfo>(
l1TokenList.tokens
.filter((token) => isValidL1(token.chainId))
.map((token) => [
getMapKey(token.chainId, token.address),
token as ArbTokenInfo,
]),
);

const l2Lists = new Map<number, ArbTokenList>();
await Promise.all(
L2_CHAIN_IDS.map(async (l2ChainId) => {
let newList = await arbifyL1List(
argvs.tokenList,
l1TokenList,
l2ChainId,
{
includeOldDataFields,
ignorePreviousList: argvs.ignorePreviousList,
prevArbifiedList: argvs.prevArbifiedList,
},
);

// Update L1 bridgeInfo for each L2 tokens
updateTokensMap(newList.tokens, tokensMap, l2ChainId);
// Add the token to tokensMap, so we can update it later with L3 bridgeInfo
newList.tokens.forEach((token) => {
tokensMap.set(getMapKey(l2ChainId, token.address), token);
});

l2Lists.set(l2ChainId, newList);
if (argvs.includePermitTags) {
newList = await addPermitTags(tokenList, l2ChainId);
}
}),
);

const l3Lists = new Map<number, ArbTokenList>();
await Promise.all(
[ChainId.Xai, ChainId.XaiTestnet, ChainId.Rari].map(async (l3ChainId) => {
const parentChain = getL3ParentChain(l3ChainId)!;
const l2TokenList = l2Lists.get(parentChain);
if (!l2TokenList) {
throw new Error(`No L2 list to arbify for L3 chain: ${l3ChainId}`);
}

const newList = await arbifyL1List(
argvs.tokenList,
l2TokenList,
l3ChainId,
{
includeOldDataFields,
ignorePreviousList: argvs.ignorePreviousList,
prevArbifiedList: argvs.prevArbifiedList,
},
);
newList.tokens.forEach((token) => {
tokensMap.set(getMapKey(l3ChainId, token.address), token);
});
// Update L1 and L2 bridgeInfo for each L3 tokens
l3Lists.set(l3ChainId, newList);
updateTokensMap(newList.tokens, tokensMap, l3ChainId);
}),
);

const tokenList: ArbTokenList = {
...l1TokenList,
tokens: Array.from(tokensMap.values()).filter((token) => token.extensions),
};

writeToFile(tokenList, argvs.newArbifiedList);
return tokenList;
};
19 changes: 13 additions & 6 deletions src/commands/full.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@ export const command = Action.Full;

export const describe = 'Full';

export const handler = async (argvs: Args) => {
if (argvs.tokenList !== 'full')
throw new Error("expected --tokenList 'full'");
if (argvs.includePermitTags)
export const handler = async ({
includePermitTags,
l2NetworkID,
newArbifiedList,
tokenList: tokenListPath,
}: Args) => {
if (tokenListPath !== 'full') throw new Error("expected --tokenList 'full'");
if (includePermitTags)
throw new Error('full list mode does not support permit tagging');
const tokenList = await generateFullList();
writeToFile(tokenList, argvs.newArbifiedList);
if (!l2NetworkID) {
throw new Error('l2NetworkID is required');
}
const tokenList = await generateFullList(l2NetworkID);
writeToFile(tokenList, newArbifiedList);
return tokenList;
};
28 changes: 20 additions & 8 deletions src/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,28 @@ export const command = Action.Update;

export const describe = 'Update';

export const handler = async (argvs: Args) => {
const includeOldDataFields = !!argvs.includeOldDataFields;
const { newList } = await updateArbifiedList(argvs.tokenList, {
includeOldDataFields,
ignorePreviousList: argvs.ignorePreviousList,
prevArbifiedList: argvs.prevArbifiedList,
export const handler = async ({
ignorePreviousList,
includeOldDataFields,
includePermitTags,
l2NetworkID,
newArbifiedList,
prevArbifiedList,
tokenList: tokenListPath,
}: Args) => {
if (!l2NetworkID) {
throw new Error('l2NetworkID is required');
}
const { newList } = await updateArbifiedList(tokenListPath, l2NetworkID, {
includeOldDataFields: !!includeOldDataFields,
ignorePreviousList,
prevArbifiedList,
});
let tokenList: ArbTokenList = newList;

if (argvs.includePermitTags) tokenList = await addPermitTags(tokenList);
writeToFile(tokenList, argvs.newArbifiedList);
if (includePermitTags) {
tokenList = await addPermitTags(tokenList, l2NetworkID);
}
writeToFile(tokenList, newArbifiedList);
return tokenList;
};
Loading