Skip to content

Commit

Permalink
feat: refactored a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
fringlesinthestreet committed Apr 30, 2024
1 parent 908a022 commit d71ad81
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 19 deletions.
15 changes: 8 additions & 7 deletions sdk/src/quotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ import {
import { signPermit } from './utils/permit';
import { getReferrerCode } from './utils/referrer';
import {
getDestinationAddressForCrosschainSwap,
getToAddressFromCrosschainQuote,
extractDestinationAddress,
sanityCheckAllowanceAddress,
sanityCheckDestinationAddress,
} from './utils/sanity_check';

/**
Expand Down Expand Up @@ -323,7 +324,7 @@ export const getCrosschainQuote = async (
const quoteWithRestrictedAllowanceTarget = quote as CrosschainQuote;
try {
quoteWithRestrictedAllowanceTarget.allowanceTarget =
getDestinationAddressForCrosschainSwap(
sanityCheckAllowanceAddress(
quoteWithRestrictedAllowanceTarget.source,
quoteWithRestrictedAllowanceTarget.chainId,
quoteWithRestrictedAllowanceTarget.allowanceTarget
Expand Down Expand Up @@ -509,10 +510,10 @@ export const fillCrosschainQuote = async (
): Promise<Transaction> => {
const { data, from, value } = quote;

const to = getDestinationAddressForCrosschainSwap(
const to = sanityCheckDestinationAddress(
quote.source,
quote.fromChainId,
getToAddressFromCrosschainQuote(quote)
extractDestinationAddress(quote)
);

let txData = data;
Expand Down Expand Up @@ -612,10 +613,10 @@ export const getCrosschainQuoteExecutionDetails = (
provider: StaticJsonRpcProvider
): CrosschainQuoteExecutionDetails => {
const { from, data, value } = quote;
const to = getDestinationAddressForCrosschainSwap(
const to = sanityCheckDestinationAddress(
quote.source,
quote.fromChainId,
getToAddressFromCrosschainQuote(quote)
extractDestinationAddress(quote)
);

return {
Expand Down
54 changes: 42 additions & 12 deletions sdk/src/utils/sanity_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@ import {
SOCKET_GATEWAY_CONTRACT_ADDRESSESS,
} from './constants';

/**
* Sanity checks the quote's allowance address against the expected address stored in the SDK.
* Relay works with an EOA an direct transfers, so no allowance is expected.
*
* @param quoteSource The aggregator used for the quote.
* @param chainID The origin network chain ID for the quote.
* @param assertedAddress The allowance address provided by the quote.
* @returns {string} The allowance address expected in the SDK for the provided (source, chainID) combination.
* @throws {Error} Throws an error if any of the following conditions are met:
* - It's a relay's quote and the quote's allowance address is not empty/undefined.
* - It's a socket's quote and the quote's allowance address is undefined or different to the expected one.
* - No destination address is defined in the SDK for the provided (source, chainID) combination.
*/
export function sanityCheckAllowanceAddress(
quoteSource: Source | undefined,
chainID: ChainId,
assertedAddress: string | undefined
): string {
const validSource = quoteSource !== undefined;
if (validSource && quoteSource === Source.CrosschainAggregatorSocket) {
return sanityCheckDestinationAddress(quoteSource, chainID, assertedAddress);
}
if (validSource && quoteSource === Source.CrosschainAggregatorRelay) {
if (assertedAddress === undefined || assertedAddress === '') {
return '';
}
throw new Error(
`relay should not bring allowance address: ${assertedAddress}`
);
}
throw new Error(`unknown crosschain swap source ${quoteSource}`);
}

/**
* 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.
Expand All @@ -19,7 +52,7 @@ import {
* - 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.
*/
export function getDestinationAddressForCrosschainSwap(
export function sanityCheckDestinationAddress(
quoteSource: Source | undefined,
chainID: ChainId,
assertedAddress: string | undefined
Expand All @@ -29,10 +62,7 @@ export function getDestinationAddressForCrosschainSwap(
`quote's allowance and to addresses must be defined (API Response)`
);
}
let expectedAddress = getStoredAddressByCrosschainSource(
quoteSource,
chainID
);
let expectedAddress = getExpectedDestinationAddress(quoteSource, chainID);
if (expectedAddress === undefined || expectedAddress === '') {
throw new Error(
`expected source ${quoteSource}'s destination address on chainID ${chainID} must be defined (Swap SDK)`
Expand Down Expand Up @@ -83,7 +113,7 @@ export function getDestinationAddressForCrosschainSwap(
* console.log(getToAddressFromCrosschainQuote(Source.CrosschainAggregatorRelay, quoteRelayERC20));
* // Output: '0xf70da97812cb96acdf810712aa562db8dfa3dbef' (assuming the call data was a ERC20 transfer)
*/
export function getToAddressFromCrosschainQuote(
export function extractDestinationAddress(
quote: CrosschainQuote
): string | undefined {
const quoteSource = quote.source;
Expand All @@ -95,7 +125,7 @@ export function getToAddressFromCrosschainQuote(
if (quote.sellTokenAddress?.toLowerCase() === ETH_ADDRESS.toLowerCase()) {
return quote.to;
}
return decodeERC20TransferData(quote.data);
return decodeERC20TransferToData(quote.data);
}
return undefined;
}
Expand All @@ -115,17 +145,17 @@ export function getToAddressFromCrosschainQuote(
* @returns { string | undefined } The destination address. If any error happens.
* Returns 'undefined' if it could not decode the call data.
*/
export function decodeERC20TransferData(
export function decodeERC20TransferToData(
data: string | undefined
): string | undefined {
if (!data?.startsWith(ERC20_TRANSFER_SIGNATURE)) {
return undefined;
}
const paramsData = data.slice(ERC20_TRANSFER_SIGNATURE.length);
if (paramsData.length >= 64 * 2) {
return `0x${paramsData.slice(0, 64).replace(/^0+/, '')}`;
if (paramsData.length < 64 * 2) {
return undefined;
}
return undefined;
return `0x${paramsData.slice(0, 64).replace(/^0+/, '')}`;
}

/**
Expand All @@ -136,7 +166,7 @@ export function decodeERC20TransferData(
* @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.
*/
export function getStoredAddressByCrosschainSource(
export function getExpectedDestinationAddress(
quoteSource: Source | undefined,
chainID: ChainId
): string | undefined {
Expand Down
98 changes: 98 additions & 0 deletions sdk/tests/utils/sanity_check.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ChainId, CrosschainQuote, EthereumAddress, Source } from '../../src';
import {
decodeERC20TransferToData,
extractDestinationAddress,
} from '../../src/utils/sanity_check';

const okERC20Data =
'0xa9059cbb000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef0000000000000000000000000000000000000000000000056bc75e2d631000000085078f';

function getQuote(
chainID: ChainId,
source: Source | undefined = undefined,
to: string | undefined = undefined,
sellTokenAddress: string | undefined = undefined,
data: string | undefined = undefined
): CrosschainQuote {
return {
buyAmount: '',
buyAmountDisplay: '',
buyAmountInEth: '',
buyAmountMinusFees: '',
buyTokenAddress: '' as EthereumAddress,
chainId: chainID,
data: data,
fee: '',
feeInEth: '',
feePercentageBasisPoints: 0,
from: '',
fromAsset: {
address: '',
chainAgnosticId: chainID,
chainId: chainID,
decimals: 18,
icon: '',
logoURI: '',
name: '',
symbol: '',
},
fromChainId: chainID,
refuel: null,
routes: [],
sellAmount: '',
sellAmountDisplay: '',
sellAmountInEth: '',
sellAmountMinusFees: '',
sellTokenAddress: sellTokenAddress as EthereumAddress,
source: source,
to: to,
toAsset: {
address: '',
chainAgnosticId: chainID,
chainId: chainID,
decimals: 18,
icon: '',
logoURI: '',
name: '',
symbol: '',
},
toChainId: 0,
tradeAmountUSD: 0,
};
}

describe('getToAddressFromCrosschainQuote', () => {
it('should return undefined if non defined source', () => {
const quote = getQuote(1, undefined, '0x1234');
expect(extractDestinationAddress(quote)).toBeUndefined();
});
it('should just use to address for socket', () => {
const quote = getQuote(1, Source.CrosschainAggregatorSocket, '0x1234');
expect(extractDestinationAddress(quote)).toEqual('0x1234');
});
});

describe('decodeERC20TransferData', () => {
it('should correctly decode valid ERC20 transfer data', () => {
expect(decodeERC20TransferToData(okERC20Data)).toEqual(
'0xf70da97812cb96acdf810712aa562db8dfa3dbef'
);
});
it('should return null for invalid data', () => {
const data = '0xdeadbeef';
expect(decodeERC20TransferToData(data)).toBeUndefined();
});
it('should return null for incomplete data', () => {
const data =
'0xa9059cbb000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef';
expect(decodeERC20TransferToData(data)).toBeUndefined();
});

it('should return decode erc20 data', () => {
const data =
'0xa9059cbb000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef000000000000000000000000000000000000000000000001a055690d9db8000000878469';
expect(decodeERC20TransferToData(data)).toEqual(
`0xf70da97812cb96acdf810712aa562db8dfa3dbef`
);
});
});

0 comments on commit d71ad81

Please sign in to comment.