Skip to content
This repository was archived by the owner on Sep 4, 2024. It is now read-only.

Add relay unit tests and e2e #338

Merged
merged 107 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from 105 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
bb9cfb3
Save state, basic implementation
zhongeric Dec 14, 2023
c797fd0
Add RelayQuoteContext and other changes
zhongeric Jan 9, 2024
f0fe0f9
fix up RelayQuote
zhongeric Jan 9, 2024
f221c4c
Fix up context
zhongeric Jan 9, 2024
b737b1e
Clean up relayQuote
zhongeric Jan 9, 2024
03f36cb
fix up relay quote structure
zhongeric Jan 9, 2024
4a0db3d
Add to parseQuoteRequests
zhongeric Jan 9, 2024
b776dbb
Clean up quote context and add conditional support
zhongeric Jan 9, 2024
66f643a
remove relay quote reparamertization
zhongeric Jan 10, 2024
65280a1
Fix:prettier
zhongeric Jan 10, 2024
a4e87a9
Fix up RelayQuoteContext
zhongeric Jan 22, 2024
2576422
Add fixtures for tests
zhongeric Jan 22, 2024
8065ec8
add comment
zhongeric Jan 22, 2024
4180d40
comment
zhongeric Jan 23, 2024
92087ca
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Jan 23, 2024
0055b06
Fix missing types
zhongeric Jan 23, 2024
f07f66c
Add some of QuoteContextHandler tests
zhongeric Jan 23, 2024
edcfa82
Add relay quote context and tests
zhongeric Jan 23, 2024
955af05
fix upo quote context tests
zhongeric Jan 23, 2024
2cc5f12
fix comment
zhongeric Jan 23, 2024
7cce69b
quoteRequest tests done
zhongeric Jan 23, 2024
45f9c6e
quoteResponse
zhongeric Jan 23, 2024
cae3cf9
Begin work on handler + handler.test.ts
zhongeric Jan 23, 2024
6391d43
Add more handler tests
zhongeric Jan 25, 2024
e61b3a8
Add classicAmountInGasAndPortionAdjusted to RelayQuote
zhongeric Jan 25, 2024
4107767
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Jan 25, 2024
05d2eba
Add RelayQuote tests
zhongeric Jan 25, 2024
4a04616
yarn fix
zhongeric Jan 25, 2024
9a8bd61
Make relay quotes 1 bp better than classic
zhongeric Jan 25, 2024
8f7451b
Add basic schema tests for relay configs
zhongeric Jan 25, 2024
9b0c9cd
Add quote comparator class
zhongeric Jan 29, 2024
04a9a28
Addd Quote comparator impl
zhongeric Jan 29, 2024
5c8f5b7
Prefer classic over relay when requested together
zhongeric Jan 31, 2024
b5a2104
Add relay abi and relay test integ
zhongeric Feb 1, 2024
2e9f19a
Merge branch 'main' into relay-quotes-tests
zhongeric Feb 1, 2024
fbc28ec
push up
zhongeric Feb 2, 2024
e8e370b
Working e2e!
zhongeric Feb 2, 2024
d5e29f4
Merge branch 'main' into relay-quotes
zhongeric Feb 2, 2024
49b9d14
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Feb 2, 2024
7dfcbbf
Add test for diff gas token
zhongeric Feb 6, 2024
f309dde
yarn build
zhongeric Feb 6, 2024
ec12c59
Add layering logic for classic requests with gasToken
zhongeric Feb 6, 2024
f3a0bf2
Merge branch 'main' into relay-quotes
zhongeric Feb 15, 2024
a62cabf
fix build issues
zhongeric Feb 15, 2024
f9b6223
update lock
zhongeric Feb 15, 2024
22b4cfc
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Feb 15, 2024
7ccd2b4
yarn fix
zhongeric Feb 15, 2024
4ed4262
fix tests
zhongeric Feb 15, 2024
c4d5947
update abi and fix tests
zhongeric Feb 15, 2024
40ddd1a
Merge branch 'main' into relay-quotes
zhongeric Mar 1, 2024
4eec6d9
yarn fix and pulling in changes from other PR
zhongeric Mar 1, 2024
f230860
yarn fix
zhongeric Mar 1, 2024
dbcf342
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Mar 1, 2024
c8812a1
fix tests around 712
zhongeric Mar 1, 2024
05b8885
Update abi and fix integ tests
zhongeric Mar 1, 2024
5dc8366
yarn fix
zhongeric Mar 1, 2024
ee0224f
Merge branch 'main' into relay-quotes
zhongeric Mar 5, 2024
ac554d9
Add nonce in fromResponseBody
zhongeric Mar 5, 2024
8224aa0
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Mar 11, 2024
42e3cc3
update abis
zhongeric Mar 12, 2024
4bb8786
fix comments
zhongeric Mar 12, 2024
08a1078
Make relay request extend classic
zhongeric Mar 18, 2024
a6571f1
Refactor - extend classic quote entities and fix data structs
zhongeric Mar 18, 2024
98dfd0f
Add local sdks to generate calldata
zhongeric Mar 20, 2024
be18ad2
Add calldata generation
zhongeric Mar 20, 2024
f053a39
Merge branch 'main' into relay-quotes
zhongeric Mar 20, 2024
310e2dc
fix v2 tests and fix lockfile
zhongeric Mar 20, 2024
361a84e
jsbi to 3.1.4
zhongeric Mar 20, 2024
38a7103
bump router-sdk and install tarball
zhongeric Mar 21, 2024
f3ea809
Fix tests
zhongeric Mar 21, 2024
411b62a
Add deadline passing to built calldata
zhongeric Mar 21, 2024
7cef3fb
fix chains
zhongeric Mar 21, 2024
7108e9e
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Mar 21, 2024
014440d
fix tests
zhongeric Mar 21, 2024
c2cef00
bump sdk to latest and fix integ tests
zhongeric Mar 21, 2024
a0cee75
Fix tests
zhongeric Mar 22, 2024
a19a14d
console.logs
zhongeric Mar 22, 2024
cf983dd
Merge branch 'main' into relay-quotes
zhongeric Mar 27, 2024
e07c26f
bump ux-sdk and fix test
zhongeric Mar 27, 2024
af757f4
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Mar 27, 2024
40fec4e
Merge branch 'main' into relay-quotes
zhongeric Apr 1, 2024
5b72335
add universal-router-sdk 1.9.0
zhongeric Apr 1, 2024
1c9c6c1
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Apr 1, 2024
098e576
Add validate on RelayQuote
zhongeric Apr 1, 2024
abf1b74
rename -> feeStartAmount/EndAmount
zhongeric Apr 1, 2024
43b0e71
Move toRouterTrade into classicQuote
zhongeric Apr 2, 2024
dc2a297
unusued funcs
zhongeric Apr 2, 2024
4b5d5a2
yarn fix
zhongeric Apr 2, 2024
0e64687
Add naive fee param
zhongeric Apr 2, 2024
49bedf8
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Apr 2, 2024
86af4d3
cody comments
zhongeric Apr 3, 2024
c107fe2
Add default buffer periods
zhongeric Apr 3, 2024
9c00914
Merge branch 'main' into relay-quotes
zhongeric Apr 3, 2024
1976b1c
fix tests
zhongeric Apr 3, 2024
3e21cff
Merge branch 'relay-quotes' into relay-quotes-tests
zhongeric Apr 4, 2024
5976fb7
fix typing and names
zhongeric Apr 4, 2024
67f5c6f
save state
zhongeric Apr 4, 2024
6b14fe8
Merge branch 'main' into relay-quotes-tests
zhongeric Apr 4, 2024
def644f
fix validator swapper field and fix tests
zhongeric Apr 4, 2024
18936f3
add comment
zhongeric Apr 4, 2024
5c6ac33
move mocks back up file
zhongeric Apr 4, 2024
87836e4
yarn fix
zhongeric Apr 4, 2024
06ac614
Merge branch 'main' into relay-quotes-tests
zhongeric Apr 11, 2024
5c1e252
add output token check
zhongeric Apr 11, 2024
87a7925
fix test
zhongeric Apr 11, 2024
0d606ac
comments
zhongeric Apr 15, 2024
415b9e8
yarn fix
zhongeric Apr 15, 2024
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
226 changes: 226 additions & 0 deletions lib/abis/RelayOrderReactor.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion lib/entities/quote/ClassicQuote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ export class ClassicQuote implements IQuote {
: BigNumber.from(this.quoteData.amount);
}

public get gasUseEstimateGasToken(): BigNumber | undefined {
public get gasUseEstimateGasToken(): BigNumber {
if (!this.quoteData.gasUseEstimateGasToken) throw new Error('No gasUseEstimateGasToken in ClassicQuote');

return BigNumber.from(this.quoteData.gasUseEstimateGasToken);
}

Expand Down
14 changes: 14 additions & 0 deletions lib/entities/request/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Protocol } from '@uniswap/router-sdk';
import { TradeType } from '@uniswap/sdk-core';
import { BigNumber } from 'ethers';

Expand Down Expand Up @@ -144,3 +145,16 @@ export function defaultRequestKey(request: QuoteRequest): string {
type: info.type,
});
}

export function parseProtocol(protocol: string): Protocol {
switch (protocol.toLowerCase()) {
case 'v2':
return Protocol.V2;
case 'v3':
return Protocol.V3;
case 'mixed':
return Protocol.MIXED;
default:
throw new Error(`Invalid protocol: ${protocol}`);
}
}
41 changes: 36 additions & 5 deletions lib/handlers/quote/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
QuoteRequest,
QuoteRequestBodyJSON,
QuoteRequestInfo,
RelayQuote,
RequestSource,
} from '../../entities';
import { TokenFetcher } from '../../fetchers/TokenFetcher';
Expand Down Expand Up @@ -339,8 +340,8 @@

protected afterResponseHook(event: APIGatewayProxyEvent, _context: Context, response: APIGatewayProxyResult): void {
const { statusCode } = response;
const responseBody = JSON.parse(response.body!);

Check warning on line 343 in lib/handlers/quote/handler.ts

View workflow job for this annotation

GitHub Actions / lint

Forbidden non-null assertion
const rawBody = JSON.parse(event.body!);

Check warning on line 344 in lib/handlers/quote/handler.ts

View workflow job for this annotation

GitHub Actions / lint

Forbidden non-null assertion

if (statusCode != 200 && responseBody.errorCode == ErrorCode.ValidationError) {
metrics.putMetric(`QuoteRequestValidationError`, 1);
Expand Down Expand Up @@ -451,27 +452,57 @@
}, null);
}

// compares two quotes of any type and returns the best one based on tradeType
export function compareQuotes(lhs: Quote, rhs: Quote, tradeType: TradeType): boolean {
export const QuoteComparisonOverrides = {
relayAndClassic(lhs: Quote, rhs: Quote): boolean {
return (
(lhs.routingType === RoutingType.CLASSIC && rhs.routingType === RoutingType.RELAY) ||
(rhs.routingType === RoutingType.CLASSIC && lhs.routingType === RoutingType.RELAY)
);
},

breakTie(lhs: Quote, rhs: Quote): boolean {
// Prefer relay over classic if requested together
if (QuoteComparisonOverrides.relayAndClassic(lhs, rhs)) return lhs.routingType === RoutingType.RELAY;
// Otherwise, we default to keeping rhs in the case of a tie
return false;
},
};

// Compare quotes, returning true if lhs is better than rhs
// Applies any overrides before the default comparision logic using quoted amount
export const compareQuotes = (lhs: Quote, rhs: Quote, tradeType: TradeType): boolean => {
if (getQuotedAmount(lhs, tradeType).eq(getQuotedAmount(rhs, tradeType))) {
return QuoteComparisonOverrides.breakTie(lhs, rhs);
}

// Default comparison if no overrides apply
if (tradeType === TradeType.EXACT_INPUT) {
return getQuotedAmount(lhs, tradeType).gt(getQuotedAmount(rhs, tradeType));
} else {
// EXACT_OUTPUT
return getQuotedAmount(lhs, tradeType).lt(getQuotedAmount(rhs, tradeType));
}
}
};

const getQuotedAmount = (quote: Quote, tradeType: TradeType) => {
if (tradeType === TradeType.EXACT_INPUT) {
if (quote.routingType === RoutingType.CLASSIC) {
return (quote as ClassicQuote).amountOutGasAndPortionAdjusted;
} else if (quote.routingType === RoutingType.DUTCH_LIMIT) {
return (quote as DutchQuote).amountOutGasAndPortionAdjusted;
} else if (quote.routingType === RoutingType.RELAY) {
return (quote as RelayQuote).classicQuote.amountOutGasAndPortionAdjusted;
}
return (quote as DutchQuote).amountOutGasAndPortionAdjusted;
throw new Error(`Invalid routing type: ${quote}`);
} else {
if (quote.routingType === RoutingType.CLASSIC) {
return (quote as ClassicQuote).amountInGasAndPortionAdjusted;
} else if (quote.routingType === RoutingType.DUTCH_LIMIT) {
return (quote as DutchQuote).amountInGasAndPortionAdjusted;
} else if (quote.routingType === RoutingType.RELAY) {
return (quote as RelayQuote).classicQuote.amountInGasAndPortionAdjusted;
}
return (quote as DutchQuote).amountInGasAndPortionAdjusted;
throw new Error(`Invalid routing type: ${quote}`);
}
};

Expand Down
7 changes: 6 additions & 1 deletion lib/handlers/quote/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@
amount: FieldValidator.amount.required(),
type: FieldValidator.tradeType.required(),
configs: Joi.array()
.items(FieldValidator.classicConfig, FieldValidator.dutchLimitConfig, FieldValidator.dutchV2Config)
.items(
FieldValidator.classicConfig,
FieldValidator.dutchLimitConfig,
FieldValidator.dutchV2Config,
FieldValidator.relayConfig
)
.unique((a: any, b: any) => {

Check warning on line 19 in lib/handlers/quote/schema.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

Check warning on line 19 in lib/handlers/quote/schema.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
return a.routingType === b.routingType;
})
.required()
Expand Down
14 changes: 13 additions & 1 deletion lib/util/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class FieldValidator {

public static readonly tradeType = Joi.string().valid('EXACT_INPUT', 'EXACT_OUTPUT');

public static readonly routingType = Joi.string().valid('CLASSIC', 'DUTCH_LIMIT', 'DUTCH_V2').messages({
public static readonly routingType = Joi.string().valid('CLASSIC', 'DUTCH_LIMIT', 'DUTCH_V2', 'RELAY').messages({
'any.only': 'Invalid routingType',
});

Expand Down Expand Up @@ -110,6 +110,18 @@ export class FieldValidator {
useSyntheticQuotes: Joi.boolean().optional(),
});

// extends a classic request config, but requires a gasToken and has optional parameters for the fee auction
public static readonly relayConfig = this.classicConfig.keys({
routingType: Joi.string().valid('RELAY'),
gasToken: FieldValidator.address.required(),
swapper: FieldValidator.address.optional(),
startTimeBufferSecs: FieldValidator.positiveNumber.optional(),
auctionPeriodSecs: FieldValidator.positiveNumber.optional(),
deadlineBufferSecs: FieldValidator.positiveNumber.optional(),
slippageTolerance: FieldValidator.slippageTolerance.optional(),
amountInGasTokenStartOverride: FieldValidator.amount.optional(),
});

public static readonly dutchV2Config = Joi.object({
routingType: Joi.string().valid('DUTCH_V2'),
swapper: FieldValidator.address.optional(),
Expand Down
11 changes: 10 additions & 1 deletion test/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Protocol } from '@uniswap/router-sdk';
import { ChainId, Currency, Ether, WETH9 } from '@uniswap/sdk-core';
import { DAI_MAINNET, USDC_MAINNET, WBTC_MAINNET } from '@uniswap/smart-order-router';
import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk';
Expand Down Expand Up @@ -49,7 +50,15 @@ export const DUTCH_V2_CONFIG = {

export const CLASSIC_CONFIG = {
routingType: RoutingType.CLASSIC,
protocols: ['V2', 'V3', 'MIXED'],
protocols: [Protocol.V2, Protocol.V3, Protocol.MIXED],
};

export const RELAY_CONFIG = {
routingType: RoutingType.RELAY,
protocols: [Protocol.V2, Protocol.V3, Protocol.MIXED],
swapper: SWAPPER,
auctionPeriodSecs: 60,
gasToken: TOKEN_IN,
};

export const PERMIT2_USED = {
Expand Down
68 changes: 61 additions & 7 deletions test/integ/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
WBTC_MAINNET,
WETH9,
} from '@uniswap/smart-order-router';
import { DutchOrder } from '@uniswap/uniswapx-sdk';
import { DutchOrder, RelayOrder } from '@uniswap/uniswapx-sdk';
import {
PERMIT2_ADDRESS,
UNIVERSAL_ROUTER_ADDRESS as UNIVERSAL_ROUTER_ADDRESS_BY_CHAIN,
Expand All @@ -20,7 +20,7 @@ import { fail } from 'assert';
import axiosStatic, { AxiosRequestConfig, AxiosResponse } from 'axios';
import axiosRetry from 'axios-retry';
import { expect } from 'chai';
import { BigNumber, providers } from 'ethers';
import { BigNumber, Contract, ContractFactory, providers } from 'ethers';
import hre from 'hardhat';
import _ from 'lodash';
import NodeCache from 'node-cache';
Expand All @@ -29,7 +29,7 @@ import { ClassicQuoteDataJSON } from '../../lib/entities/quote';
import { QuoteRequestBodyJSON } from '../../lib/entities/request';
import { Portion, PortionFetcher } from '../../lib/fetchers/PortionFetcher';
import { QuoteResponseJSON } from '../../lib/handlers/quote/handler';
import { ExclusiveDutchOrderReactor__factory, Permit2__factory } from '../../lib/types/ext';
import { ExclusiveDutchOrderReactor__factory, Permit2__factory, RelayOrderReactor__factory } from '../../lib/types/ext';
import { resetAndFundAtBlock } from '../utils/forkAndFund';
import { getBalance, getBalanceAndApprove } from '../utils/getBalanceAndApprove';
import { agEUR_MAINNET, BULLET, getAmount, UNI_MAINNET, XSGD_MAINNET } from '../utils/tokens';
Expand Down Expand Up @@ -291,17 +291,65 @@ export class BaseIntegrationTestSuite {
};
};

executeRelaySwap = async (
swapper: SignerWithAddress,
filler: SignerWithAddress,
order: RelayOrder,
currencyIn: Currency,
currencyGasToken: Currency,
currencyOut: Currency
): Promise<{
tokenInAfter: CurrencyAmount<Currency>;
tokenInBefore: CurrencyAmount<Currency>;
gasTokenAfter: CurrencyAmount<Currency>;
gasTokenBefore: CurrencyAmount<Currency>;
tokenOutAfter: CurrencyAmount<Currency>;
tokenOutBefore: CurrencyAmount<Currency>;
}> => {
const reactor = RelayOrderReactor__factory.connect(order.info.reactor, filler);

// Approve Permit2 for Alice
// Note we pass in currency.wrapped, since reactor does not support native ETH in
const tokenInBefore = await getBalanceAndApprove(swapper, PERMIT2_ADDRESS, currencyIn.wrapped);
const tokenOutBefore = await getBalance(swapper, currencyOut);
const gasTokenBefore = await getBalanceAndApprove(swapper, PERMIT2_ADDRESS, currencyGasToken.wrapped);

const { domain, types, values } = order.permitData();
const signature = await swapper._signTypedData(domain, types, values);

const transactionResponse = await reactor['execute((bytes,bytes),address)'](
{ order: order.serialize(), sig: signature },
filler.address
);
await transactionResponse.wait();

const tokenInAfter = await getBalance(swapper, currencyIn.wrapped);
const tokenOutAfter = await getBalance(swapper, currencyOut);
const gasTokenAfter = await getBalance(swapper, currencyGasToken.wrapped);

return {
tokenInAfter,
tokenInBefore,
tokenOutAfter,
tokenOutBefore,
gasTokenAfter,
gasTokenBefore,
};
};

before = async () => {
const [alice, filler] = await ethers.getSigners();
let alice: SignerWithAddress;
let filler: SignerWithAddress;
[alice, filler] = await ethers.getSigners();

// Make a dummy call to the API to get a block number to fork from.
const quoteReq: QuoteRequestBodyJSON = {
requestId: 'id',
tokenIn: 'USDC',
tokenInChainId: 1,
tokenOut: 'USDT',
tokenOut: 'DAI',
tokenOutChainId: 1,
amount: await getAmount(1, 'EXACT_INPUT', 'USDC', 'USDT', '100'),
amount: await getAmount(1, 'EXACT_INPUT', 'USDC', 'DAI', '100'),
type: 'EXACT_INPUT',
configs: [
{
Expand All @@ -318,7 +366,7 @@ export class BaseIntegrationTestSuite {

this.block = parseInt(blockNumber) - 10;

await resetAndFundAtBlock(alice, this.block, [
alice = await resetAndFundAtBlock(alice, this.block, [
parseAmount('80000000', USDC_MAINNET),
parseAmount('50000000', USDT_MAINNET),
parseAmount('100', WBTC_MAINNET),
Expand All @@ -337,4 +385,10 @@ export class BaseIntegrationTestSuite {

return [alice, filler];
};

deployContract = async (factory: ContractFactory, args: any[]): Promise<Contract> => {
const contract = await factory.deploy(...args);
await contract.deployed();
return contract;
};
}
Loading
Loading