@velora-dex/sdk
Version:
190 lines (171 loc) • 6.42 kB
text/typescript
import { assert } from 'ts-essentials';
import { ZERO_ADDRESS } from '../../common/orders/buildOrderData';
import { BeneficiaryType } from '../../common/orders/types';
import { Bridge } from './types';
export const ACROSS_WETH_ADDRESSES_MAP: Record<number, string> = {
// Mainnet
1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
// Sepolia
11155111: '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14',
// Base
8453: '0x4200000000000000000000000000000000000006',
// Base Sepolia
84532: '0x4200000000000000000000000000000000000006',
// Optimism
10: '0x4200000000000000000000000000000000000006',
// Optimism Sepolia
11155420: '0x4200000000000000000000000000000000000006',
// Polygon
137: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
// Polygon Amoy
80002: '0x52eF3d68BaB452a294342DC3e5f464d7f610f72E',
// Aleph Zero
41455: '0xB3f0eE446723f4258862D949B4c9688e7e7d35d3',
// Arbitrum
42161: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
// Arbitrum Sepolia
421614: '0x980B62Da83eFf3D4576C647993b0c1D7faf17c73',
// Boba
288: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000',
// Blast
81457: '0x4300000000000000000000000000000000000004',
// Blast Sepolia
168587773: '0x4200000000000000000000000000000000000023',
// BSC
56: '0x2170Ed0880ac9A755fd29B2688956BD959F933F8',
// Ink
57073: '0x4200000000000000000000000000000000000006',
// Ink Sepolia
763373: '0x4200000000000000000000000000000000000006',
// Lens
232: '0xE5ecd226b3032910CEaa43ba92EE8232f8237553',
// Lens Sepolia
37111: '0xaA91D645D7a6C1aeaa5988e0547267B77d33fe16',
// Linea
59144: '0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f',
// Lisk
1135: '0x4200000000000000000000000000000000000006',
// Lisk Sepolia
4202: '0x4200000000000000000000000000000000000006',
// Mode
34443: '0x4200000000000000000000000000000000000006',
// Mode Sepolia
919: '0x4200000000000000000000000000000000000006',
// Redstone
690: '0x4200000000000000000000000000000000000006',
// Scroll
534352: '0x5300000000000000000000000000000000000004',
// Scroll Sepolia
534351: '0x5300000000000000000000000000000000000004',
// Soneium
1868: '0x4200000000000000000000000000000000000006',
// Tatara
129399: '0x17B8Ee96E3bcB3b04b3e8334de4524520C51caB4',
// Unichain
130: '0x4200000000000000000000000000000000000006',
// Unichain Sepolia
1301: '0x4200000000000000000000000000000000000006',
// World Chain
480: '0x4200000000000000000000000000000000000006',
// ZK Sync
324: '0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91',
// ZK Sync Sepolia
300: '0x2D6Db36B3117802E996f13073A08A685D3FeF7eD',
// Zora
7777777: '0x4200000000000000000000000000000000000006',
};
export function isAcrossWETH(tokenAddress: string, chainId: number) {
return (
ACROSS_WETH_ADDRESSES_MAP[chainId]?.toLowerCase() ===
tokenAddress.toLowerCase()
);
}
const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
const ETH_ADDRESS_LOWERCASE = ETH_ADDRESS.toLowerCase() as Lowercase<
typeof ETH_ADDRESS
>;
export function isETHaddress(tokenAddress: string): boolean {
return tokenAddress.toLowerCase() === ETH_ADDRESS_LOWERCASE;
}
// WETH > ETH Across logic
// https://docs.across.to/introduction/technical-faq#what-is-the-behavior-of-eth-weth-in-transfers
// Velora API ref
// https://developers.velora.xyz/api/velora-api/velora-delta-api/build-a-delta-order-to-sign
export type GetDeltaBridgeAndDestTokenInput = {
destTokenDestChain: string; // the token user wants to ultimately receive
destChainId: number;
bridgeFee: string;
bridgeOutputToken: string; // the token on the destination chain, in case of WETH -> ETH, will be different from destTokenDestChain and unwrapped
beneficiaryType: BeneficiaryType;
getMulticallHandler: (chainId: number) => Promise<string>;
};
export type GetDeltaBridgeAndDestTokenOutput = {
/** @description The bridge object to be used for Order.bridge */
bridge: Bridge;
};
export async function getDeltaBridge({
destTokenDestChain,
destChainId,
bridgeFee,
bridgeOutputToken,
beneficiaryType,
getMulticallHandler,
}: GetDeltaBridgeAndDestTokenInput): Promise<GetDeltaBridgeAndDestTokenOutput> {
assert(
beneficiaryType === 'EOA' || beneficiaryType === 'SmartContract',
'beneficiaryType must be EOA or SmartContract'
);
const outputToken = bridgeOutputToken.toLowerCase(); // for uniformity
let multiCallHandler: string = ZERO_ADDRESS;
if (beneficiaryType === 'EOA' && isETHaddress(destTokenDestChain)) {
/*
if beneficiary is an EOA and destToken on destChain = ETH
order.destToken=ETH (deltaPrice already contains correct destToken)
order.bridge.outputToken=WETH_DEST_CHAIN (deltaPrice already contains correct bridge.outputToken)
order.bridge.multiCallHandler=NULL_ADDRESS
*/
multiCallHandler = ZERO_ADDRESS;
}
if (
beneficiaryType === 'EOA' &&
isAcrossWETH(destTokenDestChain, destChainId)
) {
/*
if beneficiary is an EOA and destToken on destChain = WETH
order.destToken=WETH (deltaPrice already contains correct destToken)
order.bridge.outputToken=WETH_DEST_CHAIN (deltaPrice already contains correct bridge.outputToken)
order.bridge.multiCallHandler=MULTI_CALL_HANDLER
*/
multiCallHandler = await getMulticallHandler(destChainId);
}
if (beneficiaryType === 'SmartContract' && isETHaddress(destTokenDestChain)) {
/*
if beneficiary is a contract and destToken on destChain = ETH
order.destToken=ETH (deltaPrice already contains correct destToken)
order.bridge.outputToken=WETH_DEST_CHAIN (deltaPrice already contains correct bridge.outputToken)
order.bridge.multiCallHandler=MULTI_CALL_HANDLER
*/
multiCallHandler = await getMulticallHandler(destChainId);
}
if (
beneficiaryType === 'SmartContract' &&
isAcrossWETH(destTokenDestChain, destChainId)
) {
/*
if beneficiary is a contract and destToken on destChain = WETH
order.destToken=WETH (deltaPrice already contains correct destToken)
order.bridge.outputToken=WETH_DEST_CHAIN (deltaPrice already contains correct bridge.outputToken)
order.bridge.multiCallHandler=NULL_ADDRESS
*/
multiCallHandler = ZERO_ADDRESS;
}
const bridge: Bridge = {
maxRelayerFee: bridgeFee,
destinationChainId: destChainId,
outputToken,
multiCallHandler,
};
return {
bridge,
};
}