Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: dynamic gateway list generation #42

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MAINNET_RPC="https://mainnet.infura.io/v3/<infura key>"
ARB_ONE_RPC="https://arb1.arbitrum.io/rpc"
ARB_RPC="https://arb1.arbitrum.io/rpc"
l2NetworkID=42161
PORT=3000
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@uniswap/token-lists": "^1.0.0-beta.30",
"ajv": "^8.6.3",
"ajv-formats": "^2.1.1",
"axios": "^0.23.0",
"axios": "^1.1.3",
"better-ajv-errors": "^1.1.2",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
Expand All @@ -36,7 +36,7 @@
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/node": "^16.11.1",
"@types/node": "^18.11.9",
"@types/yargs": "^17.0.4",
"@typescript-eslint/eslint-plugin": "^5.1.0",
"@typescript-eslint/parser": "^5.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/PermitTokens/multicallAbi.json
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,4 @@
"stateMutability": "nonpayable",
"type": "function"
}
]
]
93 changes: 43 additions & 50 deletions src/PermitTokens/permitSignature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import multicallAbi from '../PermitTokens/multicallAbi.json';
import { getNetworkConfig } from '../lib/instantiate_bridge';
import { getChunks, promiseRetrier } from '../lib/utils';


async function getPermitSig(
wallet: Wallet,
token: Contract,
Expand All @@ -24,7 +23,7 @@ async function getPermitSig(
// TODO: check that error is that function instead available (differentiate network fails)
const [nonce, name, version, chainId] = await Promise.all([
optional?.nonce ?? token.nonces(wallet.address).catch((err: Error) => 0),
optional?.name ?? token.name().catch((err: Error) => ""),
optional?.name ?? token.name().catch((err: Error) => ''),
optional?.version ?? '1',
optional?.chainId ?? wallet.getChainId(),
]);
Expand Down Expand Up @@ -68,8 +67,8 @@ async function getPermitSigNoVersion(
) {
// TODO: check that error is that function instead available (differentiate network fails)
const [nonce, name, chainId] = await Promise.all([
optional?.nonce ?? token.nonces(wallet.address).catch((err: Error)=> 0),
optional?.name ?? token.name().catch((err: Error) => ""),
optional?.nonce ?? token.nonces(wallet.address).catch((err: Error) => 0),
optional?.name ?? token.name().catch((err: Error) => ''),
optional?.chainId ?? wallet.getChainId(),
]);

Expand Down Expand Up @@ -111,7 +110,7 @@ async function getDaiLikePermitSignature(
// TODO: check that error is that function instead available (differentiate network fails)
const [nonce, name, chainId] = await Promise.all([
optional?.nonce ?? token.nonces(wallet.address).catch((err: Error) => 0),
optional?.name ?? token.name().catch((err: Error) => ""),
optional?.name ?? token.name().catch((err: Error) => ''),
optional?.chainId ?? wallet.getChainId(),
]);

Expand Down Expand Up @@ -154,37 +153,35 @@ enum PermitTypes {
export const addPermitTags = async (
tokenList: ArbTokenList
): Promise<ArbTokenList> => {
console.log("Adding permit tags")
console.log('Adding permit tags');
const { l1, l2 } = await getNetworkConfig();

const value = utils.parseUnits("1.0", 18);
const value = utils.parseUnits('1.0', 18);
const deadline = constants.MaxUint256;

type Call = {
tokenIndex: number,
target: string,
callData: string,
}
tokenIndex: number;
target: string;
callData: string;
};
const l1Calls: Array<Call> = [];
const l2Calls: Array<Call> = [];

const permitTokenInfo: ArbTokenInfo[] = [
...tokenList.tokens
];
const permitTokenInfo: ArbTokenInfo[] = [...tokenList.tokens];

for (let i=0; i<permitTokenInfo.length; i++) {
const curr = permitTokenInfo[i]
const isL1Token = curr.chainId === l2.network.partnerChainID
const isL2Token = curr.chainId === l2.network.chainID
if(!isL1Token && !isL2Token) continue;
for (let i = 0; i < permitTokenInfo.length; i++) {
const curr = permitTokenInfo[i];
const isL1Token = curr.chainId === l2.network.partnerChainID;
const isL2Token = curr.chainId === l2.network.chainID;
if (!isL1Token && !isL2Token) continue;

const provider = isL1Token ? l1.provider : l2.provider
const provider = isL1Token ? l1.provider : l2.provider;
const wallet = Wallet.createRandom().connect(provider);
const spender = Wallet.createRandom().connect(provider);

const tokenContract = new Contract(
curr.address,
permitTokenAbi["abi"],
permitTokenAbi['abi'],
wallet
);

Expand All @@ -196,8 +193,8 @@ export const addPermitTags = async (
deadline
);
const { v, r, s } = utils.splitSignature(signature);
const iface = new utils.Interface(permitTokenAbi["abi"]);
const callData = iface.encodeFunctionData("permit", [
const iface = new utils.Interface(permitTokenAbi['abi']);
const callData = iface.encodeFunctionData('permit', [
wallet.address,
spender.address,
value,
Expand All @@ -215,12 +212,8 @@ export const addPermitTags = async (
value,
deadline
);
const {
v: vNo,
r: rNo,
s: sNo,
} = utils.splitSignature(signatureNoVersion);
const callDataNoVersion = iface.encodeFunctionData("permit", [
const { v: vNo, r: rNo, s: sNo } = utils.splitSignature(signatureNoVersion);
const callDataNoVersion = iface.encodeFunctionData('permit', [
wallet.address,
spender.address,
value,
Expand All @@ -242,13 +235,9 @@ export const addPermitTags = async (
spender.address,
deadline
);
const {
v: vDAI,
r: rDAI,
s: sDAI,
} = utils.splitSignature(signatureDAI[0]);
const { v: vDAI, r: rDAI, s: sDAI } = utils.splitSignature(signatureDAI[0]);
const ifaceDAI = new utils.Interface(daiPermitTokenAbi);
const callDataDAI = ifaceDAI.encodeFunctionData("permit", [
const callDataDAI = ifaceDAI.encodeFunctionData('permit', [
wallet.address,
spender.address,
signatureDAI[1],
Expand Down Expand Up @@ -280,26 +269,30 @@ export const addPermitTags = async (

const handleCalls = async (calls: Array<Call>, layer: 1 | 2) => {
// TODO: use SDKs multicaller
let multiCallAddr = l2.network.tokenBridge[layer === 2 ? "l2Multicall" : "l1MultiCall"]
const isL1Mainnet = layer === 1 && l2.network.partnerChainID === 1
if(isL1Mainnet) multiCallAddr = "0x1b193bedb0b0a29c5759355d4193cb2838d2e170"

const provider = (layer === 1 ? l1 : l2).provider
const multicall = new Contract(multiCallAddr, multicallAbi, provider)
let multiCallAddr =
l2.network.tokenBridge[layer === 2 ? 'l2Multicall' : 'l1MultiCall'];
const isL1Mainnet = layer === 1 && l2.network.partnerChainID === 1;
if (isL1Mainnet)
multiCallAddr = '0x1b193bedb0b0a29c5759355d4193cb2838d2e170';

const provider = (layer === 1 ? l1 : l2).provider;
const multicall = new Contract(multiCallAddr, multicallAbi, provider);
// get array of results from tryAggregate
let tryPermit = [];
const tryPermit = [];
for (const chunk of getChunks(calls, 10)) {
console.log("handling chunk of size", chunk.length);
console.log('handling chunk of size', chunk.length);
const curr = promiseRetrier(() =>
multicall.callStatic[isL1Mainnet ? "tryAggregateGasRation" : "tryAggregate"](
multicall.callStatic[
isL1Mainnet ? 'tryAggregateGasRation' : 'tryAggregate'
](
false,
chunk.map((curr) => ({
target: curr.target,
callData: curr.callData,
}))
)
);
tryPermit.push(...await curr);
tryPermit.push(...(await curr));
}
tryPermit.flat(1);

Expand All @@ -314,16 +307,16 @@ export const addPermitTags = async (
} else {
tag = PermitTypes.NoPermit;
}
const originalIndex = calls[i].tokenIndex
const originalIndex = calls[i].tokenIndex;
// add to existing token lists w tags for all tokens (permit or no permit)
if (!permitTokenInfo[originalIndex].tags)
(permitTokenInfo[originalIndex].tags as any) = [];
permitTokenInfo[originalIndex].tags!.push(tag)
permitTokenInfo[originalIndex].tags!.push(tag);
}
}
};

await handleCalls(l1Calls, 1)
await handleCalls(l2Calls, 2)
await handleCalls(l1Calls, 1);
await handleCalls(l2Calls, 2);

return {
...tokenList,
Expand Down
84 changes: 10 additions & 74 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,13 @@


export interface L2ToL1GatewayAddresses {
[contractAddress: string]: string;
}
[contractAddress: string]: string;
}

const objKeyAndValToLowerCase = (obj: { [key: string]: string }) =>
Object.keys(obj).reduce((acc: { [key: string]: string }, key) => {
acc[key.toLowerCase()] = obj[key].toLowerCase();
return acc;
}, {});

// TODO: read these values from the gateway or a subgraph
export const l2ToL1GatewayAddresses: L2ToL1GatewayAddresses = objKeyAndValToLowerCase({
// L2 ERC20 Gateway mainnet
'0x09e9222e96e7b4ae2a407b98d48e330053351eee':
'0xa3A7B6F88361F48403514059F1F16C8E78d60EeC',
// L2 Arb-Custom Gateway mainnet
'0x096760f208390250649e3e8763348e783aef5562':
'0xcEe284F754E854890e311e3280b767F80797180d',
// L2 weth mainnet
'0x6c411ad3e74de3e7bd422b94a27770f5b86c623b':
'0xd92023E9d9911199a6711321D1277285e6d4e2db',
// L2 dai gateway mainnet
'0x467194771dae2967aef3ecbedd3bf9a310c76c65':
'0xd3b5b60020504bc3489d6949d545893982ba3011',
// L2 ERC20 Gateway rinkeby
'0x195c107f3f75c4c93eba7d9a1312f19305d6375f':
'0x91169Dbb45e6804743F94609De50D511C437572E',
// L2 Arb-Custom Gateway rinkeby
'0x9b014455acc2fe90c52803849d0002aeec184a06':
'0x917dc9a69F65dC3082D518192cd3725E1Fa96cA2',
// L2 Weth Gateway rinkeby
'0xf94bc045c4e926cc0b34e8d1c41cd7a043304ac9':
'0x81d1a19cf7071732D4313c75dE8DD5b8CF697eFD',
// old L2 weth gateway in rinkeby? we can prob remove this
'0xf90eb31045d5b924900aff29344deb42eae0b087':
'0x81d1a19cf7071732D4313c75dE8DD5b8CF697eFD',
// livepeer gateway mainnet
'0x6d2457a4ad276000a615295f7a80f79e48ccd318':
'0x6142f1C8bBF02E6A6bd074E8d564c9A5420a0676',
// Lido gateway Arb1
'0x07d4692291b9e30e326fd31706f686f83f331b82':
'0x0f25c1dc2a9922304f2eac71dca9b07e310e8e5a',

// 421613: arbstandard gateway:
'0x2ec7bc552ce8e51f098325d2fcf0d3b9d3d2a9a2':
'0x715D99480b77A8d9D603638e593a539E21345FdF',

// 421613: custom gateway:
'0x8b6990830cF135318f75182487A4D7698549C717':
'0x9fDD1C4E4AA24EEc1d913FABea925594a20d43C7',

// 421613: WETH gateway:
'0xf9F2e89c8347BD96742Cc07095dee490e64301d6':
'0x6e244cD02BBB8a6dbd7F626f05B2ef82151Ab502'
});

// nova
export const l2ToL1GatewayAddressesNova: L2ToL1GatewayAddresses =
objKeyAndValToLowerCase({
// L2 ERC20 Gateway mainnet
'0xcf9bab7e53dde48a6dc4f286cb14e05298799257':
'0xb2535b988dce19f9d71dfb22db6da744acac21bf',
// L2 Arb-Custom Gatewa mainnet
'0xbf544970e6bd77b21c6492c281ab60d0770451f4':
'0x23122da8c581aa7e0d07a36ff1f16f799650232f',
// L2 weth mainnet
'0x7626841cb6113412f9c88d3adc720c9fac88d9ed':
'0xe4e2121b479017955be0b175305b35f312330bae',
Object.keys(obj).reduce((acc: { [key: string]: string }, key) => {
acc[key.toLowerCase()] = obj[key].toLowerCase();
return acc;
}, {});

// L2 dai gateway mainnet
'0x10e6593cdda8c58a1d0f14c5164b376352a55f2f':
'0x97f63339374fce157aa8ee27830172d2af76a786',
});

export const excludeList = [
'0x0CE51000d5244F1EAac0B313a792D5a5f96931BF', //rkr
Expand All @@ -88,9 +23,10 @@ export const excludeList = [

export const SEVEN_DAYS_IN_SECONDS = 7 * 24 * 60 * 60;

export const ETHERSCAN_LIST_NAME = "EtherscanList"
export const ETHERSCAN_LIST_NAME = 'EtherscanList';

export const ETHERSCAN_PATH = process.env.PWD + '/src/FullList/all_tokens.json';
export const ALL_TOKENS_GOERLI_ROLLUP_PATH = process.env.PWD + '/src/FullList/421613_all_tokens.json';
export const ETHERSCAN_PATH = process.env.PWD + '/src/FullList/all_tokens.json';
export const ALL_TOKENS_GOERLI_ROLLUP_PATH =
process.env.PWD + '/src/FullList/421613_all_tokens.json';
export const TOKENLIST_DIR_PATH = process.env.PWD + '/src/ArbTokenLists';
export const FULLLIST_DIR_PATH = process.env.PWD + '/src/FullList';
17 changes: 9 additions & 8 deletions src/lib/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const apolloL2GatewaysRinkebyClient =
const apolloL2GatewaysClient =
'https://api.thegraph.com/subgraphs/name/fredlacs/layer2-token-gateway';

const appoloL2GatewaysGoerliRollupClient = 'https://api.thegraph.com/subgraphs/name/fredlacs/layer2-token-gateway-nitro-goerli'
const appoloL2GatewaysGoerliRollupClient =
'https://api.thegraph.com/subgraphs/name/fredlacs/layer2-token-gateway-nitro-goerli';

const chaidIdToGraphClientUrl = (chainID: string) => {
switch (chainID) {
Expand All @@ -17,7 +18,7 @@ const chaidIdToGraphClientUrl = (chainID: string) => {
case '421611':
return apolloL2GatewaysRinkebyClient;
case '421613':
return appoloL2GatewaysGoerliRollupClient
return appoloL2GatewaysGoerliRollupClient;
default:
throw new Error('Unsupported chain');
}
Expand All @@ -43,9 +44,9 @@ const isGraphTokenResult = (obj: any) => {
};

/** 421613 subgraph uses a different field name */
const graphGatewayBlockNumField = (networkID: string | number)=>{
return +networkID === 421613 ? 'l2BlockNum' : 'blockNum'
}
const graphGatewayBlockNumField = (networkID: string | number) => {
return +networkID === 421613 ? 'l2BlockNum' : 'blockNum';
};

export const getTokens = async (
tokenList: { addr: string; logo: string | undefined }[],
Expand Down Expand Up @@ -73,7 +74,7 @@ export const getTokens = async (
const formattedAddresses = tokenList
.map((token) => `"${token.addr}"`.toLowerCase())
.join(',');
const blockNumber = graphGatewayBlockNumField(_networkID)
const blockNumber = graphGatewayBlockNumField(_networkID);
const query = gql`
{
tokens(first: 500, skip: 0, where:{
Expand Down Expand Up @@ -112,7 +113,7 @@ export const getAllTokens = async (
const networkID =
typeof _networkID === 'number' ? _networkID.toString() : _networkID;
const clientUrl = chaidIdToGraphClientUrl(networkID);
const blockNumber = graphGatewayBlockNumField(_networkID)
const blockNumber = graphGatewayBlockNumField(_networkID);
const query = gql`
{
tokens(first: 500, skip: 0) {
Expand All @@ -135,7 +136,7 @@ export const getAllTokens = async (
}
`;

const { tokens } = (await request(clientUrl, query)) as GraphTokensResult;
const { tokens } = (await request(clientUrl, query)) as GraphTokensResult;
const res = tokens.map((token) => {
isGraphTokenResult(token);
return { ...token };
Expand Down
3 changes: 2 additions & 1 deletion src/lib/instantiate_bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export const getNetworkConfig = async () => {
if (networkID === 42161) return 'https://arb1.arbitrum.io/rpc';
else if (networkID === 421611) return 'https://rinkeby.arbitrum.io/rpc';
else if (networkID === 42170) return 'https://nova.arbitrum.io/rpc';
else if (networkID === 421613) return 'https://goerli-rollup.arbitrum.io/rpc'
else if (networkID === 421613)
return 'https://goerli-rollup.arbitrum.io/rpc';
throw new Error('No L2 RPC detected');
})();
const arbProvider = new providers.JsonRpcProvider(l2Rpc);
Expand Down
Loading