diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 9d3f8a3ed5..8d0163045f 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -56,6 +56,7 @@ export type RelayData = interfaces.RelayData; export type Deposit = interfaces.Deposit; export type DepositWithBlock = interfaces.DepositWithBlock; export type Fill = interfaces.Fill; +export type RelayExecutionEventInfo = interfaces.RelayExecutionEventInfo; export type FillWithBlock = interfaces.FillWithBlock; export type SpeedUp = interfaces.SpeedUp; export type SlowFillRequest = interfaces.SlowFillRequest; diff --git a/test/Dataworker.loadData.slowFill.ts b/test/Dataworker.loadData.slowFill.ts index 22037845d8..e195972b22 100644 --- a/test/Dataworker.loadData.slowFill.ts +++ b/test/Dataworker.loadData.slowFill.ts @@ -576,7 +576,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () it("Slow fill request for deposit that isn't eligible for slow fill", async function () { const invalidOutputToken = erc20_1; - const _depositObject = await depositV3( + const depositObject = await depositV3( spokePool_1, destinationChainId, depositor, @@ -585,14 +585,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () invalidOutputToken.address, amountToDeposit ); - const depositObject = { - ..._depositObject, - inputToken: toAddressType(_depositObject.inputToken, originChainId), - outputToken: toAddressType(_depositObject.outputToken, destinationChainId), - depositor: toAddressType(_depositObject.depositor, originChainId), - recipient: toAddressType(_depositObject.recipient, destinationChainId), - exclusiveRelayer: toAddressType(_depositObject.exclusiveRelayer, destinationChainId), - }; + await spokePoolClient_1.update(); await requestSlowFill(spokePool_2, relayer, depositObject); @@ -621,7 +614,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () await mockConfigStore.update(); (spokePoolClient_1 as any).configStoreClient = mockConfigStore; (spokePoolClient_2 as any).configStoreClient = mockConfigStore; - const _depositObject = await depositV3( + const depositObject = await depositV3( spokePool_1, destinationChainId, depositor, @@ -630,14 +623,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () erc20_2.address, amountToDeposit ); - const depositObject = { - ..._depositObject, - inputToken: toAddressType(_depositObject.inputToken, originChainId), - outputToken: toAddressType(_depositObject.outputToken, destinationChainId), - depositor: toAddressType(_depositObject.depositor, originChainId), - recipient: toAddressType(_depositObject.recipient, destinationChainId), - exclusiveRelayer: toAddressType(_depositObject.exclusiveRelayer, destinationChainId), - }; + await spokePoolClient_1.update(); expect(mockConfigStore.liteChainIndicesUpdates.length).to.equal(1); expect(mockConfigStore.liteChainIndicesUpdates[0].timestamp).to.be.lt(depositObject.quoteTimestamp); @@ -667,7 +653,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () await mockConfigStore.update(); (spokePoolClient_1 as any).configStoreClient = mockConfigStore; (spokePoolClient_2 as any).configStoreClient = mockConfigStore; - const _depositObject = await depositV3( + const depositObject = await depositV3( spokePool_1, destinationChainId, depositor, @@ -676,14 +662,7 @@ describe("Dataworker: Load bundle data: Computing slow fills", async function () erc20_2.address, amountToDeposit ); - const depositObject = { - ..._depositObject, - inputToken: toAddressType(_depositObject.inputToken, originChainId), - outputToken: toAddressType(_depositObject.outputToken, destinationChainId), - depositor: toAddressType(_depositObject.depositor, originChainId), - recipient: toAddressType(_depositObject.recipient, destinationChainId), - exclusiveRelayer: toAddressType(_depositObject.exclusiveRelayer, destinationChainId), - }; + await spokePoolClient_1.update(); expect(mockConfigStore.liteChainIndicesUpdates.length).to.equal(1); expect(mockConfigStore.liteChainIndicesUpdates[0].timestamp).to.be.lt(depositObject.quoteTimestamp); diff --git a/test/Dataworker.loadData.unexecutableSlowFill.ts b/test/Dataworker.loadData.unexecutableSlowFill.ts index 1e60d6dc64..08ed502496 100644 --- a/test/Dataworker.loadData.unexecutableSlowFill.ts +++ b/test/Dataworker.loadData.unexecutableSlowFill.ts @@ -238,8 +238,10 @@ describe("Dataworker: Load bundle data: Computing unexecutable slow fills", asyn await spokePoolClient_1.update(); const deposits = spokePoolClient_1.getDeposits(); expect(deposits.length).to.equal(3); - const eligibleSlowFills = depositsWithSlowFillRequests.filter((x) => erc20_2.address === x.outputToken); - const ineligibleSlowFills = depositsWithSlowFillRequests.filter((x) => erc20_2.address !== x.outputToken); + const eligibleSlowFills = depositsWithSlowFillRequests.filter((x) => erc20_2.address === x.outputToken.toNative()); + const ineligibleSlowFills = depositsWithSlowFillRequests.filter( + (x) => erc20_2.address !== x.outputToken.toNative() + ); // Generate slow fill requests for the slow fill-eligible deposits await requestSlowFill(spokePool_2, relayer, eligibleSlowFills[0]); diff --git a/test/Relayer.BasicFill.ts b/test/Relayer.BasicFill.ts index e95388390f..d01cd42c42 100644 --- a/test/Relayer.BasicFill.ts +++ b/test/Relayer.BasicFill.ts @@ -442,7 +442,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { // Make two deposits - one with the relayer as exclusiveRelayer, and one with a random address. // Verify that the relayer can immediately fill the first deposit, and both after the exclusivity window. - for (const exclusiveRelayer of [randomAddress(), relayerAddress]) { + for (const exclusiveRelayer of [randomAddress(), relayerAddress.toNative()]) { const deposit = await depositV3( spokePool_1, destinationChainId, @@ -463,8 +463,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { deposits.forEach((deposit) => { const depositHash = spokePoolClients[deposit.destinationChainId].getDepositHash(deposit); - const status = - deposit.exclusiveRelayer === relayerAddress.toEvmAddress() ? FillStatus.Filled : FillStatus.Unfilled; + const status = deposit.exclusiveRelayer.eq(relayerAddress) ? FillStatus.Filled : FillStatus.Unfilled; expect(fillStatus[depositHash] ?? FillStatus.Unfilled).to.equal(status); }); @@ -472,9 +471,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { expect((await txnReceipts[destinationChainId]).length).to.equal(0); expect(lastSpyLogIncludes(spy, "0 unfilled deposits found")).to.be.true; - const exclusiveDeposit = deposits.find( - ({ exclusiveRelayer }) => exclusiveRelayer !== relayerAddress.toEvmAddress() - ); + const exclusiveDeposit = deposits.find(({ exclusiveRelayer }) => exclusiveRelayer.eq(relayerAddress)); expect(exclusiveDeposit).to.exist; await spokePool_2.setCurrentTime(exclusiveDeposit!.exclusivityDeadline + 1); await updateAllClients(); @@ -591,10 +588,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { outputToken, outputAmount ); - const fillAmount = profitClient.getFillAmountInUsd({ - ...deposit1, - outputToken: toAddressType(deposit1.outputToken, destinationChainId), - })!; + const fillAmount = profitClient.getFillAmountInUsd(deposit1)!; expect(fillAmount).to.exist; // Simple escalating confirmation requirements; cap off with a default upper limit. @@ -644,14 +638,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { await updateAllClients(); originChainCommitment = relayerInstance.computeOriginChainCommitment(originChainId, 0, Number.MAX_SAFE_INTEGER); - expect( - originChainCommitment.eq( - getFillAmount( - { ...deposit1, outputToken: toAddressType(deposit1.outputToken, destinationChainId) }, - tokenPrice - ) - ) - ).to.be.true; + expect(originChainCommitment.eq(getFillAmount(deposit1, tokenPrice))).to.be.true; let originChainLimits = relayerInstance.computeOriginChainLimits(originChainId); expect(isChainOvercommitted(originChainLimits)).to.be.false; @@ -955,7 +942,7 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { spokePool_1, { ...deposit, - updatedRecipient: update.recipient, + updatedRecipient: toAddressType(update.recipient, destinationChainId), updatedOutputAmount: update.outputAmount, updatedMessage: update.message, }, @@ -1022,7 +1009,12 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { const updatedRecipient = randomAddress(); await updateDeposit( spokePool_1, - { ...deposit, updatedRecipient, updatedOutputAmount, updatedMessage }, + { + ...deposit, + updatedRecipient: toAddressType(updatedRecipient, destinationChainId), + updatedOutputAmount, + updatedMessage, + }, depositor ); @@ -1039,7 +1031,12 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () { updatedMessage = EMPTY_MESSAGE; await updateDeposit( spokePool_1, - { ...deposit, updatedRecipient, updatedOutputAmount, updatedMessage }, + { + ...deposit, + updatedRecipient: toAddressType(updatedRecipient, destinationChainId), + updatedOutputAmount, + updatedMessage, + }, depositor ); diff --git a/test/Relayer.SlowFill.ts b/test/Relayer.SlowFill.ts index 16ae5cc9b7..6d496a8243 100644 --- a/test/Relayer.SlowFill.ts +++ b/test/Relayer.SlowFill.ts @@ -38,7 +38,7 @@ import { winston, deployMulticall3, } from "./utils"; -import { SvmAddress, EvmAddress, toAddressType } from "../src/utils"; +import { SvmAddress, EvmAddress } from "../src/utils"; import { Relayer } from "../src/relayer/Relayer"; import { RelayerConfig } from "../src/relayer/RelayerConfig"; // Tested @@ -241,14 +241,7 @@ describe("Relayer: Initiates slow fill requests", async function () { // Verify that the slowFill request was received by the destination SpokePoolClient. await Promise.all([spokePoolClient_1.update(), spokePoolClient_2.update(), hubPoolClient.update()]); - const slowFillRequest = spokePoolClient_2.getSlowFillRequest({ - ...deposit, - inputToken: toAddressType(inputToken, originChainId), - outputToken: toAddressType(outputToken, destinationChainId), - depositor: toAddressType(deposit.depositor, originChainId), - recipient: toAddressType(deposit.recipient, destinationChainId), - exclusiveRelayer: toAddressType(deposit.exclusiveRelayer, destinationChainId), - }); + const slowFillRequest = spokePoolClient_2.getSlowFillRequest(deposit); expect(slowFillRequest).to.exist; const txnReceipts = await relayerInstance.checkForUnfilledDepositsAndFill(); diff --git a/test/Relayer.UnfilledDeposits.ts b/test/Relayer.UnfilledDeposits.ts index e84f4f7c11..3233829612 100644 --- a/test/Relayer.UnfilledDeposits.ts +++ b/test/Relayer.UnfilledDeposits.ts @@ -43,6 +43,8 @@ import { randomAddress, setupTokensForWallet, deployMulticall3, + depositIntoPrimitiveTypes, + fillIntoPrimitiveTypes, } from "./utils"; // Tested import { Relayer } from "../src/relayer/Relayer"; @@ -233,14 +235,7 @@ describe("Relayer: Unfilled Deposits", async function () { unfilledDeposits.map((unfilledDeposit) => { return { ...unfilledDeposit, - deposit: { - ...unfilledDeposit.deposit, - inputToken: unfilledDeposit.deposit.inputToken.toEvmAddress(), - outputToken: unfilledDeposit.deposit.outputToken.toEvmAddress(), - depositor: unfilledDeposit.deposit.depositor.toEvmAddress(), - recipient: unfilledDeposit.deposit.recipient.toEvmAddress(), - exclusiveRelayer: unfilledDeposit.deposit.exclusiveRelayer.toEvmAddress(), - }, + deposit: depositIntoPrimitiveTypes(unfilledDeposit.deposit), }; }) ) @@ -249,7 +244,7 @@ describe("Relayer: Unfilled Deposits", async function () { [...deposits] .sort((a, b) => (a.destinationChainId > b.destinationChainId ? 1 : -1)) .map((deposit) => ({ - deposit, + deposit: depositIntoPrimitiveTypes(deposit), unfilledAmount: deposit.outputAmount, invalidFills: [], version: configStoreClient.configStoreVersion, @@ -287,14 +282,7 @@ describe("Relayer: Unfilled Deposits", async function () { unfilledDeposits.map((unfilledDeposit) => { return { ...unfilledDeposit, - deposit: { - ...unfilledDeposit.deposit, - inputToken: unfilledDeposit.deposit.inputToken.toEvmAddress(), - outputToken: unfilledDeposit.deposit.outputToken.toEvmAddress(), - depositor: unfilledDeposit.deposit.depositor.toEvmAddress(), - recipient: unfilledDeposit.deposit.recipient.toEvmAddress(), - exclusiveRelayer: unfilledDeposit.deposit.exclusiveRelayer.toEvmAddress(), - }, + deposit: depositIntoPrimitiveTypes(unfilledDeposit.deposit), }; }) ) @@ -303,7 +291,7 @@ describe("Relayer: Unfilled Deposits", async function () { deposits .filter(({ depositId }) => depositId !== filledDeposit!.depositId) .map((deposit) => ({ - deposit, + deposit: depositIntoPrimitiveTypes(deposit), unfilledAmount: deposit.outputAmount, invalidFills: [], version: configStoreClient.configStoreVersion, @@ -333,15 +321,12 @@ describe("Relayer: Unfilled Deposits", async function () { unfilledDeposits = _getAllUnfilledDeposits(); expect( unfilledDeposits.map((unfilledDeposit) => { + const depositV3PrimitiveEvmArgs = depositIntoPrimitiveTypes(unfilledDeposit.deposit); return { ...unfilledDeposit, deposit: { ...unfilledDeposit.deposit, - inputToken: unfilledDeposit.deposit.inputToken.toEvmAddress(), - outputToken: unfilledDeposit.deposit.outputToken.toEvmAddress(), - depositor: unfilledDeposit.deposit.depositor.toEvmAddress(), - recipient: unfilledDeposit.deposit.recipient.toEvmAddress(), - exclusiveRelayer: unfilledDeposit.deposit.exclusiveRelayer.toEvmAddress(), + ...depositV3PrimitiveEvmArgs, }, invalidFills: [ { @@ -366,12 +351,14 @@ describe("Relayer: Unfilled Deposits", async function () { { deposit: { ...deposit, + ...depositIntoPrimitiveTypes(deposit), depositId: sdkUtils.toBN(deposit.depositId), }, unfilledAmount: deposit.outputAmount, invalidFills: [ { ...invalidFill, + ...fillIntoPrimitiveTypes(invalidFill), depositId: sdkUtils.toBN(invalidFill.depositId), }, ], @@ -516,14 +503,7 @@ describe("Relayer: Unfilled Deposits", async function () { outputAmount, { quoteTimestamp } ); - deposits.push({ - ...deposit, - inputToken: toAddressType(deposit.inputToken, originChainId), - outputToken: toAddressType(deposit.outputToken, destinationChainId), - depositor: toAddressType(deposit.depositor, originChainId), - recipient: toAddressType(deposit.recipient, destinationChainId), - exclusiveRelayer: toAddressType(deposit.exclusiveRelayer, destinationChainId), - }); + deposits.push(deposit); // Modify the HubPool LP balance to ensure that subsequent deposits will receive a different LP fee. const lpTokenBalance = await lpToken.balanceOf(owner.address); @@ -621,11 +601,7 @@ describe("Relayer: Unfilled Deposits", async function () { ...unfilledDeposit, deposit: { ...unfilledDeposit.deposit, - inputToken: unfilledDeposit.deposit.inputToken.toEvmAddress(), - outputToken: unfilledDeposit.deposit.outputToken.toEvmAddress(), - depositor: unfilledDeposit.deposit.depositor.toEvmAddress(), - recipient: unfilledDeposit.deposit.recipient.toEvmAddress(), - exclusiveRelayer: unfilledDeposit.deposit.exclusiveRelayer.toEvmAddress(), + ...depositIntoPrimitiveTypes(unfilledDeposit.deposit), }, invalidFills: [ { @@ -649,13 +625,14 @@ describe("Relayer: Unfilled Deposits", async function () { .to.deep.equal([ { deposit: { - ...deposit, + ...depositIntoPrimitiveTypes(deposit), depositId: sdkUtils.toBN(deposit.depositId), }, unfilledAmount: deposit.outputAmount, invalidFills: [ { ...invalidFill, + ...fillIntoPrimitiveTypes(invalidFill), depositId: sdkUtils.toBN(invalidFill.depositId), }, ], diff --git a/test/utils/SpokePoolUtils.ts b/test/utils/SpokePoolUtils.ts index 5ca6daa8af..cdd187582d 100644 --- a/test/utils/SpokePoolUtils.ts +++ b/test/utils/SpokePoolUtils.ts @@ -1,14 +1,7 @@ import assert from "assert"; -import { Contract, bnZero, spreadEvent, toBytes32 } from "../../src/utils"; -import { - Deposit, - DepositWithBlock, - Fill, - FillType, - SlowFillRequest, - SlowFillRequestWithBlock, -} from "../../src/interfaces"; -import { SignerWithAddress } from "./utils"; +import { Contract, bnZero, spreadEvent, toAddressType } from "../../src/utils"; +import { Deposit, DepositWithBlock, Fill, FillType, SlowFillRequestWithBlock } from "../../src/interfaces"; +import { SignerWithAddress, slowFillRequestFromArgs } from "./utils"; export function V3FillFromDeposit( deposit: DepositWithBlock, @@ -19,7 +12,7 @@ export function V3FillFromDeposit( const { blockNumber, txnRef, logIndex, txnIndex, quoteTimestamp, ...relayData } = deposit; const fill: Fill = { ...relayData, - relayer, + relayer: toAddressType(relayer, deposit.destinationChainId), realizedLpFeePct: deposit.realizedLpFeePct ?? bnZero, repaymentChainId: repaymentChainId ?? deposit.destinationChainId, relayExecutionInfo: { @@ -40,11 +33,11 @@ export async function requestSlowFill( await spokePool .connect(relayer) .requestSlowFill([ - toBytes32(deposit.depositor), - toBytes32(deposit.recipient), - toBytes32(deposit.exclusiveRelayer), - toBytes32(deposit.inputToken), - toBytes32(deposit.outputToken), + deposit.depositor.toBytes32(), + deposit.recipient.toBytes32(), + deposit.exclusiveRelayer.toBytes32(), + deposit.inputToken.toBytes32(), + deposit.outputToken.toBytes32(), deposit.inputAmount, deposit.outputAmount, deposit.originChainId, @@ -59,9 +52,12 @@ export async function requestSlowFill( ]); const lastEvent = events.at(-1); assert(lastEvent); + const slowFillRequest = slowFillRequestFromArgs({ + ...spreadEvent(lastEvent.args!), + destinationChainId: Number(destinationChainId), + }); const requestObject: SlowFillRequestWithBlock = { - ...(spreadEvent(lastEvent.args!) as SlowFillRequest), - destinationChainId, + ...slowFillRequest, blockNumber: lastEvent.blockNumber, txnRef: lastEvent.transactionHash, logIndex: lastEvent.logIndex, diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 1831df01a6..a0061085b9 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -9,7 +9,16 @@ import chaiExclude from "chai-exclude"; import sinon from "sinon"; import winston from "winston"; import { GLOBAL_CONFIG_STORE_KEYS } from "../../src/clients"; -import { Deposit, DepositWithBlock, Fill, FillWithBlock, SlowFillLeaf } from "../../src/interfaces"; +import { + Deposit, + DepositWithBlock, + Fill, + FillWithBlock, + RelayData, + RelayExecutionEventInfo, + SlowFillLeaf, + SlowFillRequest, +} from "../../src/interfaces"; import { BigNumber, isDefined, @@ -21,6 +30,7 @@ import { ZERO_ADDRESS, getMessageHash, toBytes32, + toAddressType, } from "../../src/utils"; import { DEFAULT_BLOCK_RANGE_FOR_CHAIN, @@ -30,6 +40,7 @@ import { } from "../constants"; import { SpokePoolDeploymentResult, SpyLoggerResult } from "../types"; import { INFINITE_FILL_DEADLINE } from "../../src/common"; +import { assert as ssAssert, number, string, boolean, optional, any, type } from "superstruct"; export { SpyTransport, @@ -227,8 +238,8 @@ export async function deployNewTokenMapping( // Give signer initial balance and approve hub pool and spoke pool to pull funds from it await addLiquidity(l1TokenHolder, hubPool, l1Token, amountToSeedLpPool); - await setupTokensForWallet(spokePool, l2TokenHolder, [l2Token, l2TokenDestination], null, 100); - await setupTokensForWallet(spokePoolDestination, l2TokenHolder, [l2TokenDestination, l2Token], null, 100); + await setupTokensForWallet(spokePool, l2TokenHolder, [l2Token, l2TokenDestination], undefined, 100); + await setupTokensForWallet(spokePoolDestination, l2TokenHolder, [l2TokenDestination, l2Token], undefined, 100); // Set time to provider time so blockfinder can find block for deposit quote time. await spokePool.setCurrentTime(await getLastBlockTime(spokePool.provider)); @@ -270,6 +281,8 @@ export async function depositV3( fillDeadline?: number; exclusivityDeadline?: number; exclusiveRelayer?: string; + fromLiteChain?: boolean; + toLiteChain?: boolean; } = {} ): Promise { const depositor = signer.address; @@ -328,19 +341,26 @@ export async function depositV3( const { blockNumber, transactionHash: txnRef, transactionIndex: txnIndex } = txnReceipt; const { logIndex } = eventLog; - const depositObject = { + const depositArgs = spreadEvent(args); + const depositObject: DepositWithBlock = { blockNumber, txnRef, txnIndex, logIndex, - ...(spreadEvent(args) as Deposit), - originChainId: Number(originChainId), + ...depositFromArgs({ + ...depositArgs, + originChainId: Number(originChainId), + messageHash: args.messageHash ?? getMessageHash(args.message), + fromLiteChain: opts.fromLiteChain ?? false, + toLiteChain: opts.toLiteChain ?? false, + }), quoteBlockNumber: 0, - messageHash: args.messageHash ?? getMessageHash(args.message), }; + if (isLegacyDeposit) { - depositObject.outputToken = outputToken; + depositObject.outputToken = toAddressType(outputToken, originChainId); } + return depositObject; } @@ -350,7 +370,10 @@ export async function updateDeposit( depositor: SignerWithAddress ): Promise { const { updatedRecipient: updatedRecipientAddress, updatedOutputAmount, updatedMessage } = deposit; - const updatedRecipient = sdkUtils.toBytes32(updatedRecipientAddress!); + if (updatedRecipientAddress === undefined) { + throw `updateDeposit cannot have updatedRecipientAddress undefined ${depositIntoPrimitiveTypes(deposit)}`; + } + const updatedRecipient = updatedRecipientAddress.toBytes32(); assert.ok(isDefined(updatedRecipient)); assert.ok(isDefined(updatedOutputAmount)); assert.ok(isDefined(updatedMessage)); @@ -387,11 +410,11 @@ export async function fillV3Relay( await spokePool.connect(signer).fillRelay( { ...deposit, - depositor: toBytes32(deposit.depositor), - recipient: toBytes32(deposit.recipient), - inputToken: toBytes32(deposit.inputToken), - outputToken: toBytes32(deposit.outputToken), - exclusiveRelayer: toBytes32(deposit.exclusiveRelayer), + depositor: deposit.depositor.toBytes32(), + recipient: deposit.recipient.toBytes32(), + inputToken: deposit.inputToken.toBytes32(), + outputToken: deposit.outputToken.toBytes32(), + exclusiveRelayer: deposit.exclusiveRelayer.toBytes32(), }, repaymentChainId ?? destinationChainId, toBytes32(await signer.getAddress()) @@ -407,17 +430,11 @@ export async function fillV3Relay( const parsedEvent = spreadEvent(args); return { - destinationChainId, blockNumber, txnRef: transactionHash, txnIndex: transactionIndex, logIndex, - ...(parsedEvent as Fill), - messageHash: args.messageHash ?? getMessageHash(args.message), - relayExecutionInfo: { - ...parsedEvent.relayExecutionInfo, - updatedMessageHash: getMessageHash(parsedEvent.relayExecutionInfo.updatedMessage), - }, + ...fillFromArgs({ ...parsedEvent, destinationChainId }), }; } @@ -434,7 +451,7 @@ export async function addLiquidity( await hubPool.connect(signer).addLiquidity(l1Token.address, amount); } -export function buildV3SlowRelayLeaves(deposits: interfaces.Deposit[], lpFeePct: BigNumber): SlowFillLeaf[] { +export function buildV3SlowRelayLeaves(deposits: Deposit[], lpFeePct: BigNumber): SlowFillLeaf[] { const chainId = deposits[0].destinationChainId; assert.isTrue(deposits.every(({ destinationChainId }) => chainId === destinationChainId)); return deposits @@ -491,3 +508,185 @@ export function createRefunds( }, }; } + +// Superstruct validation schemas to ensure expected properties exist in argument maps. +const RelayDataFields = { + originChainId: number(), + depositor: string(), + recipient: string(), + depositId: any(), + inputToken: string(), + inputAmount: any(), + outputToken: string(), + outputAmount: any(), + message: string(), + fillDeadline: any(), + exclusiveRelayer: string(), + exclusivityDeadline: any(), +}; + +// This one isn't from the SDK, but a requirement for the test util helpers +const DestinationChainIdField = { + destinationChainId: number(), +}; + +// The raw args for relay data need destinationChainId for creating Address types +const RelayDataArgsStruct = type({ ...RelayDataFields, ...DestinationChainIdField }); + +// Now for the specific event types +const { message, ...OmittedMessageRelayDataFields } = RelayDataFields; + +const BaseRelayArgsStruct = type({ + ...OmittedMessageRelayDataFields, + ...DestinationChainIdField, +}); + +const DepositArgsStruct = type({ + ...RelayDataFields, + ...DestinationChainIdField, + messageHash: string(), + quoteTimestamp: number(), + fromLiteChain: boolean(), + toLiteChain: boolean(), + speedUpSignature: optional(string()), + updatedRecipient: optional(string()), + updatedOutputAmount: optional(any()), + updatedMessage: optional(string()), +}); + +const RelayExecutionEventInfoStruct = type({ + updatedRecipient: string(), + updatedOutputAmount: any(), // BigNumber + updatedMessage: optional(string()), + updatedMessageHash: string(), + fillType: any(), // enum FillType +}); + +const FillArgsStruct = type({ + ...OmittedMessageRelayDataFields, + ...DestinationChainIdField, + messageHash: string(), + relayer: string(), + repaymentChainId: number(), + relayExecutionInfo: RelayExecutionEventInfoStruct, +}); + +const SlowFillRequestArgsStruct = type({ + ...OmittedMessageRelayDataFields, + ...DestinationChainIdField, + messageHash: string(), +}); + +function _getRelayDataFields(relayDataArgs: { [key: string]: any }): Omit { + ssAssert(relayDataArgs, BaseRelayArgsStruct); + return { + originChainId: relayDataArgs.originChainId, + depositor: toAddressType(relayDataArgs.depositor, relayDataArgs.originChainId), + recipient: toAddressType(relayDataArgs.recipient, relayDataArgs.destinationChainId), + depositId: relayDataArgs.depositId, + inputToken: toAddressType(relayDataArgs.inputToken, relayDataArgs.originChainId), + inputAmount: relayDataArgs.inputAmount, + outputToken: toAddressType(relayDataArgs.outputToken, relayDataArgs.destinationChainId), + outputAmount: relayDataArgs.outputAmount, + fillDeadline: relayDataArgs.fillDeadline, + exclusiveRelayer: toAddressType(relayDataArgs.exclusiveRelayer, relayDataArgs.destinationChainId), + exclusivityDeadline: relayDataArgs.exclusivityDeadline, + }; +} + +// A helper function to parse key - value map into a Fill object +export function fillFromArgs(fillArgs: { [key: string]: any }): Fill { + ssAssert(fillArgs, FillArgsStruct); + const relayData = _getRelayDataFields(fillArgs); + const { relayExecutionInfo: relayExecutionInfoArgs } = fillArgs; + const relayExecutionInfo: RelayExecutionEventInfo = { + updatedRecipient: toAddressType(relayExecutionInfoArgs.updatedRecipient, fillArgs.destinationChainId), + updatedOutputAmount: relayExecutionInfoArgs.updatedOutputAmount, + updatedMessageHash: relayExecutionInfoArgs.updatedMessageHash, + fillType: relayExecutionInfoArgs.fillType, + }; + if (relayExecutionInfoArgs.updatedMessage) { + relayExecutionInfo.updatedMessage = relayExecutionInfoArgs.updatedMessage; + } + return { + ...relayData, + messageHash: fillArgs.messageHash, + destinationChainId: fillArgs.destinationChainId, + relayer: toAddressType(fillArgs.relayer, fillArgs.destinationChainId), + repaymentChainId: fillArgs.repaymentChainId, + relayExecutionInfo, + }; +} + +// decomposes Fill into primitive types suitable for === comparions +export function fillIntoPrimitiveTypes(fill: Fill) { + return { + ...fill, + inputToken: fill.inputToken.toNative(), + outputToken: fill.outputToken.toNative(), + depositor: fill.depositor.toNative(), + recipient: fill.recipient.toNative(), + exclusiveRelayer: fill.exclusiveRelayer.toNative(), + relayer: fill.relayer.toNative(), + relayExecutionInfo: { + ...fill.relayExecutionInfo, + updatedRecipient: fill.relayExecutionInfo.updatedRecipient?.toNative(), + }, + }; +} + +export function relayDataFromArgs(relayDataArgs: { [key: string]: any }): RelayData { + ssAssert(relayDataArgs, RelayDataArgsStruct); + return { + ..._getRelayDataFields(relayDataArgs), + message: relayDataArgs.message, + }; +} + +export function slowFillRequestFromArgs(slowFillRequestArgs: { [key: string]: any }): SlowFillRequest { + ssAssert(slowFillRequestArgs, SlowFillRequestArgsStruct); + const relayData = _getRelayDataFields(slowFillRequestArgs); + return { + ...relayData, + destinationChainId: slowFillRequestArgs.destinationChainId, + messageHash: slowFillRequestArgs.messageHash, + }; +} + +// A helper function to parse key - value map into a Deposit object with correct types (e.g. Address) +export function depositFromArgs(depositArgs: { [key: string]: any }): Deposit { + ssAssert(depositArgs, DepositArgsStruct); + const deposit: Deposit = { + ...relayDataFromArgs(depositArgs), + destinationChainId: depositArgs.destinationChainId, + messageHash: depositArgs.messageHash, + quoteTimestamp: depositArgs.quoteTimestamp, + fromLiteChain: depositArgs.fromLiteChain, + toLiteChain: depositArgs.toLiteChain, + }; + + if (depositArgs.speedUpSignature) { + deposit.speedUpSignature = depositArgs.speedUpSignature; + } + + if (depositArgs.updatedRecipient) { + deposit.updatedRecipient = toAddressType(depositArgs.updatedRecipient, depositArgs.destinationChainId); + deposit.updatedOutputAmount = depositArgs.updatedOutputAmount; + deposit.updatedMessage = depositArgs.updatedMessage; + } + + return deposit; +} + +// decomposes Deposit into primitive types suitable for === comparions +export function depositIntoPrimitiveTypes(deposit: Deposit) { + return { + ...deposit, + inputToken: deposit.inputToken.toNative(), + outputToken: deposit.outputToken.toNative(), + depositor: deposit.depositor.toNative(), + recipient: deposit.recipient.toNative(), + exclusiveRelayer: deposit.exclusiveRelayer.toNative(), + updatedRecipient: deposit.updatedRecipient?.toNative(), + }; +}