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

Crosschainswaps V3 #71

Merged
merged 14 commits into from
May 2, 2024
2 changes: 1 addition & 1 deletion sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.18.0",
"version": "0.19.0-beta.0",
fringlesinthestreet marked this conversation as resolved.
Show resolved Hide resolved
"name": "@rainbow-me/swaps",
"license": "GPL-3.0",
"main": "dist/index.js",
Expand Down
80 changes: 74 additions & 6 deletions sdk/src/quotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
PERMIT_EXPIRATION_TS,
RAINBOW_ROUTER_CONTRACT_ADDRESS,
RAINBOW_ROUTER_CONTRACT_ADDRESS_ZORA,
RELAY_LINK_BRIDGING_RELAYER_ADDRESS,
SOCKET_GATEWAY_CONTRACT_ADDRESSESS,
WRAPPED_ASSET,
} from './utils/constants';
Expand Down Expand Up @@ -148,7 +149,7 @@ const buildRainbowCrosschainQuoteUrl = ({
swapType: SwapType.crossChain,
toChainId: String(toChainId),
});
return `${API_BASE_URL}/v1/quote?bridgeVersion=2&` + searchParams.toString();
return `${API_BASE_URL}/v1/quote?bridgeVersion=3&` + searchParams.toString();
};

/**
Expand Down Expand Up @@ -278,7 +279,9 @@ export const getQuote = async (
* @param {BigNumberish} params.sellAmount
* @param {number} params.slippage
* @param {boolean} params.refuel
* @returns {Promise<CrosschainQuote | null>}
* @returns {Promise<CrosschainQuote | QuoteError | null>} returns error in case the request failed or the
* destination address is not consistent with the SDK's
* stored destination address
*/
export const getCrosschainQuote = async (
params: QuoteParams
Expand Down Expand Up @@ -316,12 +319,77 @@ export const getCrosschainQuote = async (
}

const quoteWithRestrictedAllowanceTarget = quote as CrosschainQuote;
quoteWithRestrictedAllowanceTarget.allowanceTarget =
SOCKET_GATEWAY_CONTRACT_ADDRESSESS.get(chainId);
try {
quoteWithRestrictedAllowanceTarget.allowanceTarget = getDestinationAddressForCrosschainSwap(
quoteWithRestrictedAllowanceTarget.source,
quoteWithRestrictedAllowanceTarget.chainId,
quoteWithRestrictedAllowanceTarget.allowanceTarget
);
} catch (e) {
return {
error: true,
message:
e instanceof Error
? e.message
: `unexpected error happened while checking crosschain quote's address: ${quoteWithRestrictedAllowanceTarget.allowanceTarget}`,
} as QuoteError;
}

return quoteWithRestrictedAllowanceTarget;
};

/**
* Sanity checks the quote's returned address against the expected address stored in the SDK.
* This function ensures the integrity and correctness of the destination address provided by the quote source.
*
* @param quoteSource - The aggregator used for the quote.
* @param chainID - The origin network chain ID for the quote.
* @param assertedAddress - The destination address provided by the quote.
* @returns {string} The destination address stored in the SDK for the provided (source, chainID) combination.
* @throws {Error} Throws an error if any of the following conditions are met:
* - The quote's destination address is undefined.
* - No destination address is defined in the SDK for the provided (source, chainID) combination.
* - The provided quote's destination address does not case-insensitively match the SDK's stored destination address.
*/
const getDestinationAddressForCrosschainSwap = (
quoteSource: Source | undefined,
chainID: ChainId,
assertedAddress: string | undefined,
): string => {
if (assertedAddress === undefined || assertedAddress === "") {
throw new Error(`quote's allowance and to addresses must be defined (API Response)`);
}
let expectedAddress = getStoredAddressByCrosschainSource(quoteSource, chainID);
if (expectedAddress === undefined || expectedAddress === "") {
throw new Error(`expected source ${quoteSource}'s destination address on chainID ${chainID} must be defined (Swap SDK)`);
}
if (expectedAddress.toLowerCase() !== assertedAddress?.toLowerCase()) {
throw new Error(`source ${quoteSource}'s destination address '${assertedAddress}' on chainID ${chainID} is not consistent, expected: '${expectedAddress}'`);
}
return expectedAddress!.toString()
}

/**
* Retrieves the destination address stored in the SDK corresponding to the specified aggregator and chain ID.
*
* @param quoteSource - The aggregator used for the quote.
* @param chainID - The origin network chain ID for the quote.
* @returns {string | undefined} The destination address stored in the SDK for the provided (source, chainID) combination.
* Returns `undefined` if no address is stored for the specified combination.
*/
const getStoredAddressByCrosschainSource = (
quoteSource: Source | undefined,
chainID: ChainId,
): string | undefined => {
const validSource = quoteSource !== undefined;
if (validSource && quoteSource == Source.CrosschainAggregatorSocket) {
return SOCKET_GATEWAY_CONTRACT_ADDRESSESS.get(chainID);
} else if (validSource && quoteSource == Source.CrosschainAggregatorRelay) {
return RELAY_LINK_BRIDGING_RELAYER_ADDRESS;
}
return undefined
}

const calculateDeadline = async (wallet: Wallet) => {
const { timestamp } = await wallet.provider.getBlock('latest');
return timestamp + PERMIT_EXPIRATION_TS;
Expand Down Expand Up @@ -490,7 +558,7 @@ export const fillCrosschainQuote = async (
): Promise<Transaction> => {
const { data, from, value } = quote;

const to = SOCKET_GATEWAY_CONTRACT_ADDRESSESS.get(quote.fromChainId);
const to = getDestinationAddressForCrosschainSwap(quote.source, quote.fromChainId, quote.to);

let txData = data;
if (referrer) {
Expand Down Expand Up @@ -589,7 +657,7 @@ export const getCrosschainQuoteExecutionDetails = (
provider: StaticJsonRpcProvider
): CrosschainQuoteExecutionDetails => {
const { from, data, value } = quote;
const to = SOCKET_GATEWAY_CONTRACT_ADDRESSESS.get(quote.fromChainId);
const to = getDestinationAddressForCrosschainSwap(quote.source, quote.fromChainId, quote.to);

return {
method: provider.estimateGas({
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ export enum ChainId {
export enum Source {
Aggregator0x = '0x',
Aggregator1inch = '1inch',
AggregatorRainbow = 'rainbow',
// DEPRECATED: Use Aggregator1inch instead
Aggregotor1inch = '1inch',

// Crosschain
CrosschainAggregatorSocket = 'socket',
CrosschainAggregatorRelay = 'relay',
}

export enum SwapType {
Expand Down
5 changes: 4 additions & 1 deletion sdk/src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BigNumber } from '@ethersproject/bignumber';
import { ChainId, EthereumAddress } from '../types';
export const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
export const API_BASE_URL = 'https://swap.p.rainbow.me';
export const API_BASE_URL = 'https://swap.s.rainbow.me';
fringlesinthestreet marked this conversation as resolved.
Show resolved Hide resolved
export const RAINBOW_ROUTER_CONTRACT_ADDRESS =
'0x00000000009726632680fb29d3f7a9734e3010e2';

Expand All @@ -21,6 +21,9 @@ export const SOCKET_GATEWAY_CONTRACT_ADDRESSESS = new Map([
[ChainId.blast, '0x3a23F943181408EAC424116Af7b7790c94Cb97a5'],
]);

// RELAY_LINK_BRIDGING_RELAYER_ADDRESS is the EOA used by relay link as relayer on all chains
export const RELAY_LINK_BRIDGING_RELAYER_ADDRESS = '0xf70da97812CB96acDF810712Aa562db8dfA3dbEF';

export type MultiChainAsset = {
[key: string]: EthereumAddress;
};
Expand Down