Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion ccip-cli/src/commands/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export async function showRequests(ctx: Ctx, argv: Parameters<typeof handler>[0]
offRamp,
messageId: request.message.messageId,
sourceChainSelector: request.message.sourceChainSelector,
startTime: request.tx.timestamp,
startTime: request.log.blockTimestamp,
verifications: !argv.wait ? await verifications$ : undefined,
watch: argv.wait && ctx.abort,
})
Expand Down
32 changes: 22 additions & 10 deletions ccip-cli/src/commands/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,23 @@ tokenTransfers =\t[${req.message.tokenAmounts.map((ta) => ('token' in ta ? ta.to
* @returns Object with Date timestamp.
*/
export function withDateTimestamp<
T extends { readonly timestamp: number } | { readonly tx: { readonly timestamp: number } },
T extends
| { readonly timestamp: number }
| { readonly blockTimestamp: number }
| { readonly log: { readonly blockTimestamp: number } }
| { readonly tx: { readonly timestamp: number } },
>(obj: T): Omit<T, 'timestamp'> & { timestamp: Date } {
return {
...obj,
timestamp: new Date(('timestamp' in obj ? obj.timestamp : obj.tx.timestamp) * 1e3),
timestamp: new Date(
('timestamp' in obj
? obj.timestamp
: 'blockTimestamp' in obj
? obj.blockTimestamp
: 'log' in obj
? obj.log.blockTimestamp
: obj.tx.timestamp) * 1e3,
),
}
}

Expand Down Expand Up @@ -333,11 +345,11 @@ export async function prettyRequest(this: Ctx, request: CCIPRequest, source?: Ch
transactionHash: displayTxHash,
logIndex: request.log.index,
blockNumber: request.log.blockNumber,
timestamp: `${formatDate(request.tx.timestamp)} (${formatDuration(Date.now() / 1e3 - request.tx.timestamp)} ago)`,
timestamp: `${formatDate(request.log.blockTimestamp)} (${formatDuration(Date.now() / 1e3 - request.log.blockTimestamp)} ago)`,
finalized:
finalized &&
(finalized < request.tx.timestamp
? formatDuration(request.tx.timestamp - finalized) + ' left'
(finalized < request.log.blockTimestamp
? formatDuration(request.log.blockTimestamp - finalized) + ' left'
: true),
fee: await formatToken(source, {
token: request.message.feeToken,
Expand Down Expand Up @@ -366,7 +378,7 @@ export async function prettyVerifications(
this: Ctx,
dest: Chain,
verifications: CCIPVerifications,
request: PickDeep<CCIPRequest, 'tx.timestamp' | 'lane.destChainSelector'>,
request: PickDeep<CCIPRequest, 'log.blockTimestamp' | 'lane.destChainSelector'>,
) {
const destFamily = networkInfo(request.lane.destChainSelector).family

Expand All @@ -383,7 +395,7 @@ export async function prettyVerifications(
contract: formatDisplayAddress(verifications.log.address, destFamily),
transactionHash: formatDisplayTxHash(verifications.log.transactionHash, destFamily),
blockNumber: verifications.log.blockNumber,
timestamp: `${formatDate(timestamp)} (${formatDuration(timestamp - request.tx.timestamp)} after request)`,
timestamp: `${formatDate(timestamp)} (${formatDuration(timestamp - request.log.blockTimestamp)} after request)`,
})
} else {
let ts = 0
Expand All @@ -393,7 +405,7 @@ export async function prettyVerifications(
prettyTable.call(this, {
...verifications,
...(ts && {
timestamp: `${formatDate(ts)} (${formatDuration(ts - request.tx.timestamp)} after request)`,
timestamp: `${formatDate(ts)} (${formatDuration(ts - request.log.blockTimestamp)} after request)`,
}),
})
}
Expand Down Expand Up @@ -515,7 +527,7 @@ export function prettyTable(
export function prettyReceipt(
this: Ctx,
receipt: CCIPExecution,
request: PickDeep<CCIPRequest, 'tx.timestamp' | 'lane.destChainSelector'>,
request: PickDeep<CCIPRequest, 'log.blockTimestamp' | 'lane.destChainSelector'>,
origin?: string,
) {
const destFamily = networkInfo(request.lane.destChainSelector).family
Expand All @@ -532,7 +544,7 @@ export function prettyReceipt(
transactionHash: formatDisplayTxHash(receipt.log.transactionHash, destFamily),
logIndex: receipt.log.index,
blockNumber: receipt.log.blockNumber,
timestamp: `${formatDate(receipt.timestamp)} (${formatDuration(receipt.timestamp - request.tx.timestamp)} after request)`,
timestamp: `${formatDate(receipt.log.blockTimestamp)} (${formatDuration(receipt.log.blockTimestamp - request.log.blockTimestamp)} after request)`,
})
}

Expand Down
1 change: 1 addition & 0 deletions ccip-sdk/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ export class CCIPAPIClient {
topics: [lane.version < CCIPVersion.V1_6 ? 'CCIPSendRequested' : 'CCIPMessageSent'],
index: Number(sendLogIndex),
blockNumber: Number(sendBlockNumber),
blockTimestamp: sendTimestamp_,
}

// Build tx from API data
Expand Down
23 changes: 5 additions & 18 deletions ccip-sdk/src/aptos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from '@aptos-labs/ts-sdk'
import { type BytesLike, concat, isBytesLike, isHexString } from 'ethers'
import { memoize } from 'micro-memoize'
import type { PickDeep } from 'type-fest'

import {
type ChainContext,
Expand Down Expand Up @@ -77,7 +76,7 @@ import {
} from '../utils.ts'
import { getTokenInfo } from './token.ts'
import type { CCIPMessage_V1_6_EVM } from '../evm/messages.ts'
import { buildMessageForDest, decodeMessage, getMessagesInBatch } from '../requests.ts'
import { buildMessageForDest, decodeMessage } from '../requests.ts'
export type { UnsignedAptosTx }

/**
Expand Down Expand Up @@ -140,7 +139,7 @@ export class AptosChain extends Chain<typeof ChainFamily.Aptos> {
{
maxSize: 100,
async: true,
transformKey: ([arg]) => [(arg as { ledgerVersion: number }).ledgerVersion],
transformKey: ([arg]: [{ ledgerVersion: bigint | number }]) => [Number(arg.ledgerVersion)],
},
)
}
Expand Down Expand Up @@ -216,16 +215,18 @@ export class AptosChain extends Chain<typeof ChainFamily.Aptos> {
}
if (tx.type !== TransactionResponseType.User) throw new CCIPAptosTransactionTypeInvalidError()

const timestamp = +tx.timestamp / 1e6
return {
hash: tx.hash,
blockNumber: +tx.version,
from: tx.sender,
timestamp: +tx.timestamp / 1e6,
timestamp,
logs: tx.events.map((event, index) => ({
address: event.type.slice(0, event.type.lastIndexOf('::')),
transactionHash: tx.hash,
index,
blockNumber: +tx.version, // we use version as Aptos' blockNumber, as blockHeight isn't very useful
blockTimestamp: timestamp,
data: event.data as Record<string, unknown>,
topics: [event.type.slice(event.type.lastIndexOf('::') + 2)],
})),
Expand All @@ -246,20 +247,6 @@ export class AptosChain extends Chain<typeof ChainFamily.Aptos> {
yield* streamAptosLogs(this, opts)
}

/** {@inheritDoc Chain.getMessagesInBatch} */
override async getMessagesInBatch<
R extends PickDeep<
CCIPRequest,
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
>,
>(
request: R,
range: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
opts?: { page?: number },
): Promise<R['message'][]> {
return getMessagesInBatch(this, request, range, opts)
}

/** {@inheritDoc Chain.typeAndVersion} */
async typeAndVersion(address: string) {
// requires address with `::<module>` suffix
Expand Down
1 change: 1 addition & 0 deletions ccip-sdk/src/aptos/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export async function* streamAptosLogs(
? `${ev.version}`
: (await getUserTxByVersion(ctx.provider, +ev.version)).hash,
data: ev.data as Record<string, unknown>,
blockTimestamp: await getVersionTimestamp(ctx.provider, +ev.version),
}
}
}
25 changes: 5 additions & 20 deletions ccip-sdk/src/canton/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { type BytesLike, id as keccak256Utf8 } from 'ethers'
import type { PickDeep } from 'type-fest'

import {
type ChainContext,
Expand All @@ -23,7 +22,6 @@ import { CCV_INDEXER_URL } from '../evm/const.ts'
import type { ExtraArgs } from '../extra-args.ts'
import type { LeafHasher } from '../hasher/common.ts'
import { type NetworkInfo, ChainFamily, networkInfo } from '../networks.ts'
import { getMessagesInBatch } from '../requests.ts'
import { supportedChains } from '../supported-chains.ts'
import {
type CCIPExecution,
Expand Down Expand Up @@ -322,6 +320,7 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
transactionHash: hash,
index,
blockNumber: tx.offset,
blockTimestamp: timestamp,
topics: templateId ? [templateId] : [],
data: inner,
}
Expand All @@ -344,22 +343,6 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
throw new CCIPNotImplementedError('CantonChain.getLogs')
}

/**
* {@inheritDoc Chain.getMessagesInBatch}
*/
override async getMessagesInBatch<
R extends PickDeep<
CCIPRequest,
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
>,
>(
request: R,
commit: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
opts?: { page?: number },
): Promise<R['message'][]> {
return getMessagesInBatch(this, request, commit, opts)
}

/**
* {@inheritDoc Chain.typeAndVersion}
* @throws {@link CCIPNotImplementedError} always (not yet implemented for Canton)
Expand Down Expand Up @@ -795,6 +778,7 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
index: 0,
address: '',
blockNumber: 0,
blockTimestamp: 0,
transactionHash: updateId,
data: response.transaction,
}
Expand Down Expand Up @@ -1053,12 +1037,13 @@ export class CantonChain extends Chain<typeof ChainFamily.Canton> {
topics: [],
index: 0,
address: '',
blockNumber: 0,
blockNumber: response.transaction.offset,
blockTimestamp: timestamp,
transactionHash: updateId,
data: response.transaction,
}

return { receipt, log, timestamp }
return { receipt, log }
}

/**
Expand Down
43 changes: 20 additions & 23 deletions ccip-sdk/src/chain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type BytesLike, dataLength, keccak256 } from 'ethers'
import type { PickDeep, SetOptional } from 'type-fest'
import type { PickDeep } from 'type-fest'

import { type LaneLatencyResponse, CCIPAPIClient } from './api/index.ts'
import type { UnsignedAptosTx } from './aptos/types.ts'
Expand Down Expand Up @@ -32,7 +32,7 @@ import type { LeafHasher } from './hasher/common.ts'
import { decodeMessageV1 } from './messages.ts'
import { type ChainFamily, type NetworkInfo, networkInfo } from './networks.ts'
import { getOffchainTokenData } from './offchain.ts'
import { getMessagesInRange, getMessagesInTx } from './requests.ts'
import { getMessagesInBatch, getMessagesInRange, getMessagesInTx } from './requests.ts'
import { DEFAULT_GAS_LIMIT } from './shared/constants.ts'
import type { UnsignedSolanaTx } from './solana/types.ts'
import type { UnsignedSuiTx } from './sui/types.ts'
Expand Down Expand Up @@ -682,23 +682,18 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
* ```
*/
async waitFinalized({
request: { log, tx },
request: { log },
finality = 'finalized',
abort,
}: {
request: SetOptional<
PickDeep<
CCIPRequest,
| `log.${'address' | 'blockNumber' | 'transactionHash' | 'topics' | 'tx.timestamp'}`
| 'tx.timestamp'
>,
'tx'
request: PickDeep<
CCIPRequest,
`log.${'address' | 'blockNumber' | 'transactionHash' | 'topics' | 'blockTimestamp'}`
>
finality?: number | 'finalized'
abort?: AbortSignal
}): Promise<true> {
const timestamp = log.tx?.timestamp ?? tx?.timestamp
if (!timestamp || Date.now() / 1e3 - timestamp > 60) {
if (!log.blockTimestamp || Date.now() / 1e3 - log.blockTimestamp > 60) {
// only try to fetch tx if request is old enough (>60s)
const [trans, finalizedTs] = await Promise.all([
this.getTransaction(log.transactionHash),
Expand Down Expand Up @@ -889,16 +884,20 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
* console.log(`Found ${messages.length} messages in batch`)
* ```
*/
getMessagesInBatch?<
getMessagesInBatch<
R extends PickDeep<
CCIPRequest,
'lane' | `log.${'topics' | 'address' | 'blockNumber'}` | 'message.sequenceNumber'
| 'lane'
| `log.${'topics' | 'address' | 'blockNumber' | 'blockTimestamp'}`
| 'message.sequenceNumber'
>,
>(
request: R,
range: Pick<CommitReport, 'minSeqNr' | 'maxSeqNr'>,
opts?: { page?: number },
): Promise<R['message'][]>
): Promise<R['message'][]> {
return getMessagesInBatch(this, request, range, opts)
}

/**
* Fetch input data needed for executing messages
Expand All @@ -924,7 +923,7 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
}
// other messages in same batch are available from `source` side;
// not needed for chain families supporting only >=v2
const messagesInBatch = await this.getMessagesInBatch!(request, verifications.report, opts)
const messagesInBatch = await this.getMessagesInBatch(request, verifications.report, opts)
const execReportProof = calculateManualExecProof(
messagesInBatch,
request.lane,
Expand Down Expand Up @@ -1368,7 +1367,7 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
/** CCIPRequest subset object */
request: PickDeep<
CCIPRequest,
'lane' | `message.${'sequenceNumber' | 'messageId'}` | 'tx.timestamp'
'lane' | `message.${'sequenceNumber' | 'messageId'}` | 'log.blockTimestamp'
>
} & Pick<LogFilter, 'page' | 'watch' | 'startBlock'>): Promise<CCIPVerifications> {
return getOnchainCommitReport(this, offRamp, request, hints)
Expand Down Expand Up @@ -1516,8 +1515,7 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
)
continue

const timestamp = log.tx?.timestamp ?? (await this.getBlockTimestamp(log.blockNumber))
yield { receipt, log, timestamp }
yield { receipt, log }
if (receipt.state === ExecutionState.Success) break
}
}
Expand All @@ -1540,11 +1538,10 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
async getExecutionReceiptInTx(tx: string | ChainTransaction): Promise<CCIPExecution> {
if (typeof tx === 'string') tx = await this.getTransaction(tx)
for (const log of tx.logs) {
const rcpt = (this.constructor as ChainStatic).decodeReceipt(log)
if (!rcpt) continue
const receipt = (this.constructor as ChainStatic).decodeReceipt(log)
if (!receipt) continue

const timestamp = tx.timestamp
return { receipt: rcpt, log, timestamp }
return { receipt, log }
}
throw new CCIPExecTxRevertedError(tx.hash)
}
Expand Down
Loading
Loading