Skip to content

Instantly share code, notes, and snippets.

@bitcoinski
Created December 24, 2024 15:36
across.ts
import { acrossClient, acrossRoutes, bridgeState, updateBridgeStep } from '$lib/stores/across';
import { PUBLIC_CONTRACT_ADDRESS, PUBLIC_ENVIRONMENT } from '$env/static/public';
import type { Amount, CrossChainAction, Quote } from '@across-protocol/app-sdk';
import { get } from 'svelte/store';
import { encodeFunctionData, type Address } from 'viem';
import { getWalletClient } from '@wagmi/core';
import { signerAddress, wagmiConfig } from '$lib/stores/appkit';
import { base, baseSepolia } from 'viem/chains';
import { INTENTIONS_ABI, BASE_ETH_ABI } from '$lib/constants';
const isDev = ['staging', 'localhost'].includes(PUBLIC_ENVIRONMENT);
// https://github.com/across-protocol/contracts/blob/master/deployments/deployments.json
const ACROSS_CONTRACTS = {
8453: {
SpokePool: { address: '0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64', blockNumber: 2164878 },
SpokePoolVerifier: {
address: '0xB4A8d45647445EA9FC3E1058096142390683dBC2',
blockNumber: 12285703
},
'1inch_SwapAndBridge': {
address: '0x7CFaBF2eA327009B39f40078011B0Fb714b65926',
blockNumber: 14450808
},
UniswapV3_SwapAndBridge: {
address: '0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4',
blockNumber: 14450714
},
MulticallHandler: {
address: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
blockNumber: 16917922
}
},
84532: {
SpokePool: { address: '0x82B564983aE7274c86695917BBf8C99ECb6F0F8F', blockNumber: 6082004 },
MulticallHandler: {
address: '0x924a9f036260DdD5808007E1AA95f08eD08aA569',
blockNumber: 12429513
}
}
};
export const getQuote = async (
inputAmount: bigint,
originChainId: number,
inputTokenSymbol: string,
metadata: string,
signature: any
) => {
if (typeof inputAmount !== 'bigint') throw new Error('Input amount must be a bigint');
//if (inputAmount <= 0n) throw new Error('Input amount must be greater than 0');
if (!PUBLIC_CONTRACT_ADDRESS) throw new Error('PUBLIC_CONTRACT_ADDRESS not found');
const client = get(acrossClient);
if (!client) throw new Error('Across client not initialized');
const routes = get(acrossRoutes);
// console.log('ROUTES', routes);
const route = routes.find(
(route) =>
route.originChainId === originChainId &&
route.destinationChainId === (isDev ? baseSepolia.id : base.id) &&
route.inputTokenSymbol === inputTokenSymbol
);
if (!route) throw new Error('Route not found');
console.log('ROUTE::', route);
// console.log('FOUND ROUTE', route);
const userAddress = get(signerAddress) as `0x${string}`;
if (!userAddress) throw new Error('User address not found');
// console.log('METADATA', metadata);
// console.log('SIGNATURE', signature);
// console.log('INPUT AMOUNT', inputAmount);
//
const BASE_ETH = {
chain: isDev ? baseSepolia : base,
abi: BASE_ETH_ABI,
address: '0x4200000000000000000000000000000000000006' as `0x${string}`,
decimals: 18,
symbol: 'ETH',
name: 'ETH'
};
const INTENTIONS = {
address: PUBLIC_CONTRACT_ADDRESS as `0x${string}`,
abi: INTENTIONS_ABI,
chain: isDev ? baseSepolia : base,
decimals: 18,
symbol: 'ETH',
name: 'ETH'
};
// WETH unwrap action
function generateUnwrapCallData(ethAmount: Amount) {
return encodeFunctionData({
abi: BASE_ETH.abi,
functionName: 'withdraw',
args: [BigInt(ethAmount)]
});
}
// Mint action
function generateMintCallData(userAddress: Address, metadata: string, signature: any) {
// console.log('METADATA', metadata);
// console.log('SIGNATURE::', signature);
return encodeFunctionData({
abi: INTENTIONS.abi,
functionName: 'relaySignatureToCreate',
args: [userAddress,metadata, signature]
});
}
const unwrapAndMintMessage: {
actions: CrossChainAction[];
fallbackRecipient: Address;
} = {
actions: [
{
target: BASE_ETH.address,
callData: generateUnwrapCallData(inputAmount),
value: 0n,
update: (updatedOutputAmount: Amount) => {
return {
callData: generateUnwrapCallData(updatedOutputAmount)
};
}
},
{
target: INTENTIONS.address,
callData: generateMintCallData(userAddress, metadata, signature),
value: inputAmount,
update: (updatedOutputAmount: Amount) => {
console.log('updatedOutputAmount', updatedOutputAmount);
return {
value: updatedOutputAmount
};
}
}
],
fallbackRecipient: userAddress
};
const quoteResponse = await client.getQuote({
inputAmount: inputAmount.toString(),
route,
recipient: ACROSS_CONTRACTS[isDev ? baseSepolia.id : base.id].MulticallHandler
.address as `0x${string}`,
crossChainMessage: unwrapAndMintMessage
});
console.log('QUOTE RESPONSE::', quoteResponse);
return quoteResponse;
};
export const executeQuote = async (quote: Quote, pendingCallback?: () => void) => {
const client = get(acrossClient);
if (!client) throw new Error('Across client not initialized');
const walletClient = await getWalletClient(get(wagmiConfig));
bridgeState.update(state => ({ ...state, isAcrossBridge: true }));
return await client.executeQuote({
walletClient,
deposit: quote.deposit,
onProgress: (progress) => {
console.log('progress:::', progress);
if (progress.step === 'approve') {
updateBridgeStep('approve', progress.status === 'txPending' ? 'pending' : 'success', {
txHash: progress.txHash,
details: progress.txReceipt && {
blockNumber: progress.txReceipt.blockNumber,
gasUsed: progress.txReceipt.gasUsed?.toString()
}
});
}
if (progress.step === 'deposit') {
updateBridgeStep('deposit', progress.status === 'txPending' ? 'pending' : 'success', {
txHash: progress.txHash,
details: progress.txReceipt && {
blockNumber: progress.txReceipt.blockNumber,
gasUsed: progress.txReceipt.gasUsed?.toString()
}
});
if (progress.status === 'txPending' && pendingCallback) pendingCallback();
}
if (progress.step === 'fill') {
updateBridgeStep('fill', progress.status === 'txPending' ? 'pending' : 'success', {
txHash: progress.txHash,
details: {
...(progress.txReceipt && {
blockNumber: progress.txReceipt.blockNumber,
gasUsed: progress.txReceipt.gasUsed?.toString()
}),
...(progress.status === 'txSuccess' && {
fillTxTimestamp: progress.fillTxTimestamp,
actionSuccess: progress.actionSuccess
})
}
});
}
}
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment