Skip to content

Conversation

@PabloMansanet
Copy link
Contributor

@PabloMansanet PabloMansanet commented Jan 5, 2026

Unit tested and tested manually through a localhost deployment of the latest CCIP API:

image

What this is

This PR introduces a method to the CCIP API Client that requests a message by messageId, agnostic about any other environment data (CCIP message IDs are unique so this will work across chains and onramps).

To verify the behaviour, I also added a CLI command in the same style as the one for getLatency. The fields exposed by it are a smaller subset but can be expanded to be more informative if needed.

Return type concerns

Currently, the return type of the method is a composite of a Partial form of CCIPRequest, and a Metadata type. The reasons for this are:

  • Not everything defined inside CCIPRequest is available from the API alone.
  • Some useful info that can be retrieved from the API is not in CCIPRequest.

This works fine for now (and could be merged as-is if needed) but ideally we iterate on this until we find a clean common type that we can reuse directly, with the tradeoff of having some optional fields. Reaching this will likely require tweaks on both sides. Here are the rough edges that I found, and how I think we should tackle them:

  • The MessageStatus enum (SDK side) does not yet support 1.7 statuses, where the one in the API does. This is fine and shouldn't be a blocker, because the enums are completely compatible otherwise. Before the SDK supports 1.7, an attempt to retrieve a 1.7 message will fail on validation, which is the expected behaviour.
  • TokenAmounts in the API currently only include the token address on source + the transferred amount, where the SDK expects detailed info including source pool address, destination pool address, extraData and destGasAmount. We could expose this in the API, but I wanted to check with @andrevmatos whether there's a usecase for these fields or whether we can KISS for now. EDIT: Opened a jira ticket to handle the API change needed to expose this data. This has now been implemented so the API returns all fields required by the SDK.
  • There's a few fields (what I named APICCIPRequestMetadata) which are retrieved from the API and don't exist directly in the CCIPRequest type. I have no strong opinion on whether these should be integrated into CCIPRequest or should remain on the side.
  • There are some mismatches in optionality between the CCIPRequest type fields and the API responses. For example, the nonce or onramp may not exist in the API response where CCIPRequest expects it. In case of absence, I've defaulted them to empty values for this draft, but it would be good to make informed decisions on why they're optional or not in either side, unify the ones that can be unified, and converge on what to do otherwise.

@vercel
Copy link

vercel bot commented Jan 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
ccip-tools-ts Error Error Jan 18, 2026 4:41pm

Request Review

return raw.map((ta) => ({
token: ta.tokenAddress,
amount: BigInt(ta.amount),
// TODO: API does not provide pool addresses - these require on-chain lookup

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss what's the best approach here.

)
}

const url = `${this.baseUrl}/v1/messages/${encodeURIComponent(messageId)}`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep in mind the order of dependencies between the different components and their rollout plans before merging this.

Copy link
Contributor Author

@PabloMansanet PabloMansanet Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retargeted the PR base to feat/APIv2 (A staging branch for the purpose of behaviour that only works with the new schema)

EDIT: Retargeted to main again after our conversation in the sync. It's fine to merge behaviour that targets the V2 API to main.

@github-actions
Copy link

github-actions bot commented Jan 7, 2026

Coverage Report

ℹ tests 689
ℹ suites 207
ℹ pass 689
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 186134.484854
-------------------------------|---------|----------|---------|---------|---------------------------
File                           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s         
-------------------------------|---------|----------|---------|---------|---------------------------
All files                      |   67.21 |    81.07 |   56.56 |   67.21 |                           
 ccip-cli/src                  |   86.45 |    83.33 |      50 |   86.45 |                           
  index.ts                     |   86.45 |    83.33 |      50 |   86.45 | 72-76,82-83,88-93         
 ccip-cli/src/commands         |   51.79 |    79.55 |   51.02 |   51.79 |                           
  get-message.ts               |   26.66 |      100 |       0 |   26.66 | ...1,38-45,49-126,133-135 
  index.ts                     |     100 |      100 |     100 |     100 |                           
  lane-latency.ts              |   67.74 |     87.5 |   33.33 |   67.74 | 23-37,44-51,85-91         
  manual-exec.ts               |      21 |      100 |       0 |      21 | 42-106,113-123,125-236    
  parse.ts                     |   41.79 |      100 |       0 |   41.79 | 22-26,33-40,42-67         
  send.ts                      |   12.38 |      100 |       0 |   12.38 | 34-136,143-150,152-323    
  show.ts                      |   89.61 |    63.82 |      60 |   89.61 | ...23-129,135-137,146-147 
  supported-tokens.ts          |   23.48 |      100 |       0 |   23.48 | ...83-175,177-232,234-247 
  types.ts                     |     100 |      100 |     100 |     100 |                           
  utils.ts                     |   86.59 |    84.03 |    91.3 |   86.59 | ...99-517,524-532,542-548 
 ccip-cli/src/providers        |   53.94 |       75 |   17.39 |   53.94 |                           
  aptos.ts                     |   53.65 |      100 |       0 |   53.65 | ...70,78-85,91-92,101-123 
  evm.ts                       |   46.03 |      100 |       0 |   46.03 | 20,31-63                  
  index.ts                     |   82.95 |    72.09 |      80 |   82.95 | 49-50,188-223             
  solana.ts                    |   50.42 |      100 |       0 |   50.42 | ...-71,79-85,89-90,99-117 
  sui.ts                       |   64.28 |      100 |       0 |   64.28 | 10-14                     
  ton.ts                       |   14.58 |      100 |       0 |   14.58 | 22-144                    
 ccip-sdk/src                  |   93.29 |    88.94 |   91.52 |   93.29 |                           
  chain.ts                     |   92.91 |     86.2 |      75 |   92.91 | ...32,251-289,577,635-644 
  commits.ts                   |     100 |       90 |     100 |     100 | 35,52                     
  execution.ts                 |   84.61 |      100 |      75 |   84.61 | 122-143                   
  explorer.ts                  |     100 |      100 |     100 |     100 |                           
  extra-args.ts                |     100 |    85.71 |     100 |     100 | 71,90                     
  gas.ts                       |   96.42 |    92.85 |     100 |   96.42 | 81-85                     
  http-status.ts               |     100 |      100 |     100 |     100 |                           
  index.ts                     |     100 |      100 |     100 |     100 |                           
  offchain.ts                  |     100 |      100 |     100 |     100 |                           
  requests.ts                  |   78.84 |    77.61 |      75 |   78.84 | ...14-315,319-320,343-364 
  supported-chains.ts          |     100 |      100 |     100 |     100 |                           
  types.ts                     |     100 |      100 |     100 |     100 |                           
  utils.ts                     |   95.11 |    90.62 |     100 |   95.11 | ...51,515-516,532,597-605 
 ccip-sdk/src/api              |   99.52 |    93.44 |     100 |   99.52 |                           
  index.ts                     |   99.52 |    93.44 |     100 |   99.52 | 244-245                   
 ccip-sdk/src/aptos            |   57.89 |    69.65 |      60 |   57.89 |                           
  exec.ts                      |   29.31 |      100 |       0 |   29.31 | 18-58                     
  hasher.ts                    |   76.31 |       80 |   66.66 |   76.31 | 19-38,52-58               
  index.ts                     |   58.91 |    73.49 |   62.22 |   58.91 | ...16-751,755-848,852-863 
  logs.ts                      |   78.52 |    57.44 |      75 |   78.52 | ...90-196,200-233,264-268 
  send.ts                      |    25.2 |      100 |       0 |    25.2 | 10-51,62-79,92-123        
  token.ts                     |   23.75 |       75 |     100 |   23.75 | 35-156                    
  types.ts                     |   69.72 |      100 |       0 |   69.72 | 25-32,77-101              
  utils.ts                     |     100 |      100 |     100 |     100 |                           
 ccip-sdk/src/errors           |   81.06 |    77.33 |      50 |   81.06 |                           
  CCIPError.ts                 |     100 |      100 |     100 |     100 |                           
  codes.ts                     |     100 |      100 |     100 |     100 |                           
  index.ts                     |     100 |      100 |     100 |     100 |                           
  recovery.ts                  |     100 |      100 |     100 |     100 |                           
  specialized.ts               |    73.4 |    72.97 |   47.22 |    73.4 | ...51,1659-1668,1676-1685 
  utils.ts                     |   94.52 |    77.77 |     100 |   94.52 | 16,18,23,25               
 ccip-sdk/src/evm              |   64.83 |    78.82 |   62.85 |   64.83 |                           
  const.ts                     |     100 |      100 |     100 |     100 |                           
  errors.ts                    |   82.48 |    78.46 |     100 |   82.48 | ...55-157,188-195,198-216 
  hasher.ts                    |     100 |     87.5 |     100 |     100 | 122                       
  index.ts                     |   58.45 |    84.61 |   58.49 |   58.45 | ...21,1325-1398,1402-1451 
  logs.ts                      |   33.99 |    46.66 |      25 |   33.99 | ...13-214,229-258,274-302 
  messages.ts                  |     100 |      100 |     100 |     100 |                           
  offchain.ts                  |    80.4 |    53.33 |      75 |    80.4 | ...45-147,151-165,191-196 
 ccip-sdk/src/evm/viem         |   77.72 |       92 |   69.23 |   77.72 |                           
  client-adapter.ts            |     100 |     92.3 |     100 |     100 | 48                        
  index.ts                     |     100 |      100 |     100 |     100 |                           
  wallet-adapter.ts            |   63.09 |     90.9 |   55.55 |   63.09 | ...6,53-73,91-124,131-157 
 ccip-sdk/src/hasher           |   94.29 |    78.94 |     100 |   94.29 |                           
  common.ts                    |     100 |      100 |     100 |     100 |                           
  hasher.ts                    |     100 |    66.66 |     100 |     100 | 19                        
  index.ts                     |     100 |      100 |     100 |     100 |                           
  merklemulti.ts               |   93.43 |       78 |     100 |   93.43 | ...59-260,306-307,315-316 
 ccip-sdk/src/solana           |   52.43 |     70.1 |   56.32 |   52.43 |                           
  cleanup.ts                   |   12.22 |      100 |       0 |   12.22 | 29-229                    
  exec.ts                      |    9.27 |      100 |       0 |    9.27 | ...66-341,343-382,384-496 
  hasher.ts                    |   96.58 |    81.81 |     100 |   96.58 | 67-70                     
  index.ts                     |   71.47 |    76.11 |   64.81 |   71.47 | ...33,1437-1461,1465-1486 
  logs.ts                      |   74.19 |    27.27 |     100 |   74.19 | ...,50-51,53-54,56-88,119 
  offchain.ts                  |   78.87 |     64.7 |      50 |   78.87 | ...,96-97,110-111,126-142 
  patchBorsh.ts                |   65.06 |    57.14 |      75 |   65.06 | 30-49,65-66,72-78         
  send.ts                      |   14.84 |      100 |       0 |   14.84 | ...63-252,265-331,333-384 
  types.ts                     |   74.46 |      100 |       0 |   74.46 | 36-47                     
  utils.ts                     |   54.14 |    77.77 |      60 |   54.14 | ...71-284,326-389,395-410 
 ccip-sdk/src/sui              |   33.42 |    93.47 |   14.54 |   33.42 |                           
  discovery.ts                 |   16.56 |      100 |       0 |   16.56 | 18-34,44-157              
  events.ts                    |   30.21 |      100 |       0 |   30.21 | ...83-154,199-288,296-321 
  hasher.ts                    |   98.16 |    66.66 |     100 |   98.16 | 33,49                     
  index.ts                     |   38.63 |      100 |   12.19 |   38.63 | ...19-720,724-725,729-730 
  objects.ts                   |   12.77 |      100 |       0 |   12.77 | ...05-146,157-207,218-360 
 ccip-sdk/src/sui/manuallyExec |   39.46 |      100 |       0 |   39.46 |                           
  encoder.ts                   |   47.67 |      100 |       0 |   47.67 | 42-86                     
  index.ts                     |    34.3 |      100 |       0 |    34.3 | 48-137                    
 ccip-sdk/src/ton              |   88.26 |    87.64 |   73.52 |   88.26 |                           
  exec.ts                      |     100 |      100 |     100 |     100 |                           
  hasher.ts                    |   77.95 |    77.77 |      75 |   77.95 | 99-107,155-186            
  index.ts                     |   94.09 |    88.78 |   67.39 |   94.09 | ...56,1060-1061,1065-1066 
  logs.ts                      |     100 |    98.52 |     100 |     100 | 56                        
  types.ts                     |   78.41 |    69.23 |   66.66 |   78.41 | ...-76,94,121-134,136-139 
  utils.ts                     |   75.98 |       75 |     100 |   75.98 | ...55-261,273-327,329-332 
-------------------------------|---------|----------|---------|---------|---------------------------

@PabloMansanet PabloMansanet changed the base branch from main to feat/APIv2 January 7, 2026 09:40
@PabloMansanet PabloMansanet marked this pull request as ready for review January 7, 2026 09:40
@PabloMansanet PabloMansanet requested a review from a team as a code owner January 7, 2026 09:40
@PabloMansanet PabloMansanet force-pushed the retrieve_from_api_by_ID branch from e56402a to 4ccdb3e Compare January 7, 2026 10:20
@PabloMansanet PabloMansanet requested a review from aelmanaa January 9, 2026 10:07
@PabloMansanet PabloMansanet changed the base branch from feat/APIv2 to main January 12, 2026 13:19
@andrevmatos andrevmatos deleted the retrieve_from_api_by_ID branch January 12, 2026 16:56
@andrevmatos andrevmatos restored the retrieve_from_api_by_ID branch January 12, 2026 17:03
@andrevmatos andrevmatos reopened this Jan 12, 2026
@andrevmatos andrevmatos restored the retrieve_from_api_by_ID branch January 12, 2026 17:11
@andrevmatos andrevmatos reopened this Jan 12, 2026
@aelmanaa
Copy link
Collaborator

Also, we need to update the changelog and cli/sdk under /docs folder

})

it('should throw CCIPApiClientNotAvailableError when --no-api flag is set', async () => {
it('should throw CCIPApiClientNotAvailableError when --noapi flag is set', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @aelmanaa I renamed --no-api to --noapi because the arg parser takes the former as a negation of the api flag, which doesn't exist, and that breaks the command.

Not sure if this came up in testing before, but --no-api won't work unless we add --api.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay @PabloMansanet - just please make sure all the mentions to --no-api are replaced then. I can see in line 368 of /docs/cli/index.md

**Note:** This command requires CCIP API access. It will fail if `--no-api` flag is used.

@PabloMansanet PabloMansanet changed the title Expose message retrieval by ID (API) as a CCIPAPIClient method Expose message retrieval by ID or TxHash (API) Jan 18, 2026
`Failed to retrieve message ${messageId} via API and RPC.\n API: ${apiMsg}\n RPC: ${rpcMsg}`,
{
...options,
isTransient: apiError?.isTransient ?? false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isTransient: apiError?.isTransient ?? false,
isTransient: apiError?.isTransient || rpcError?.isTransient || false

if API is disabled , the error is an rpcone (we should check isTransient )


this.logger.debug(`CCIPAPIClient: GET ${url}`)

const response = await this._fetch(url)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API client methods (getMessageById, getMessageIdsInTx, getLaneLatency) currently have no timeout protection - a slow or unresponsive API could cause indefinite hangs. Consider adding a fetchWithTimeout helper using AbortController - example:
private async fetchWithTimeout(url: string, timeoutMs = 30000): Promise
The timeout could be configurable via CCIPAPIClientContext for users with specific latency requirements.

what do you think?

})

it('should throw CCIPApiClientNotAvailableError when --no-api flag is set', async () => {
it('should throw CCIPApiClientNotAvailableError when --noapi flag is set', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay @PabloMansanet - just please make sure all the mentions to --no-api are replaced then. I can see in line 368 of /docs/cli/index.md

**Note:** This command requires CCIP API access. It will fail if `--no-api` flag is used.

let getChain: ChainGetter | undefined
const ensureChains = async () => {
if (!getChain) {
getChain = fetchChainsFromRpcs(ctx, argv)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT (okay for now because I don't see any parallelism): ensureChains memoizes the result but not the Promise (while fetchChainsFromRpcs seems to be a pretty slow operation). Currently safe since calls are sequential, but consider memoizing the Promise for future-proofing if parallel calls are ever added.

CCIPTransactionNotFoundError,
CCIPMessageIdNotFoundError,
CCIPMessageIdValidationError,
CCIPMessageRetrievalError,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling section documents errors for getMessageById, but getMessageIdsInTx throws additional errors that aren't documented: CCIPMessageNotFoundInTxError, CCIPUnexpectedPaginationError, CCIPArgumentInvalidError. Could you consider adding these for consistency?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants