Skip to content

fix(cct-sdk): Prevent partial Solana CCT commits - #416

Open
mervin-link wants to merge 3 commits into
cct-sdkfrom
fix/DAPP-11383-set-token-authority-truncation
Open

fix(cct-sdk): Prevent partial Solana CCT commits#416
mervin-link wants to merge 3 commits into
cct-sdkfrom
fix/DAPP-11383-set-token-authority-truncation

Conversation

@mervin-link

@mervin-link mervin-link commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

What

  • For more info: DAPP-11383
  • Add CCT-local Solana submission that splits only locally oversized transaction slices
  • Rethrow simulation/program errors without truncating instructions
  • Preserve setTokenAuthority atomicity and validate selected mint/freeze authorities before submission
  • Report confirmed split transaction hashes in CCTTxFailedError.context.committedHashes

Why

  • Prevent a failed multi-instruction CCT operation from committing an earlier instruction while returning an error

Notes

  • CCT uses a local simulateAndSendTxs helper because shared Solana version treats every simulation failure as a size failure and splits instructions. The local path splits only after a deterministic size check, preserving CCT operation semantics without changing shared SDK behavior.
  • Operations can pass requireSingleTransaction to reject oversize rather than split when they require atomic execution.

@mervin-link
mervin-link requested a review from a team as a code owner September 9, 2026 18:05
@vercel

vercel Bot commented Sep 9, 2026

Copy link
Copy Markdown

You must have Developer access to commit code to Chainlink Labs on Vercel. If you contact an administrator and receive Developer access, commit again to see your changes.

Learn more: https://vercel.com/docs/accounts/team-members-and-roles/access-roles#team-level-roles

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

CI Test Report

3 of 2579 tests did not pass (pass 2570, fail 3, cancelled 0, skipped 6) in 5m 53s

Failed suites: ccip-sdk/src/cct/solana/operation.test.ts, ccip-sdk/src/evm/integration.test.ts

Failed tests
test at ccip-sdk/src/cct/solana/operation.test.ts:89:3
✖ uses wallet public key as payer without mutating caller params (158.433058ms)
  Error [CCTTxFailedError]: testOperation failed: transaction has no main instruction
      at simulateAndSendCCTTxs (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/submit.ts:137:26)
      at submit (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/submit.ts:172:19)
      at TestOperation.execute (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/operation.ts:59:12)
      at async TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/operation.test.ts:97:5)
      at async Test.run (node:internal/test_runner/test:1404:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:969:7) {
    _isCCIPError: true,
    code: 'CCT_TX_FAILED',
    context: { operation: 'testOperation', reason: 'transaction has no main instruction' },
    isTransient: false,
    retryAfterMs: undefined,
    recovery: 'The CCT transaction failed. Ensure the caller holds the required role for this operation.',
    [cause]: undefined
  }

test at ccip-sdk/src/cct/solana/operation.test.ts:104:3
✖ does not require payer on signed execution params (1.766888ms)
  Error [CCTTxFailedError]: testOperation failed: transaction has no main instruction
      at simulateAndSendCCTTxs (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/submit.ts:137:26)
      at submit (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/submit.ts:172:19)
      at TestOperation.execute (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/operation.ts:59:12)
      at async TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/cct/solana/operation.test.ts:111:5)
      at async Test.run (node:internal/test_runner/test:1404:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:969:7) {
    _isCCIPError: true,
    code: 'CCT_TX_FAILED',
    context: { operation: 'testOperation', reason: 'transaction has no main instruction' },
    isTransient: false,
    retryAfterMs: undefined,
    recovery: 'The CCT transaction failed. Ensure the caller holds the required role for this operation.',
    [cause]: undefined
  }

test at ccip-sdk/src/evm/integration.test.ts:364:5
✖ should return fee tokens for a v1.5 OnRamp on base-sepolia (659.315173ms)
  AssertionError [ERR_ASSERTION]: base-sepolia → chiado should be a legacy OnRamp
  + actual - expected
  
  + 'OnRamp'
  - 'EVM2EVMOnRamp'
  
      at TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/evm/integration.test.ts:370:14)
      at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      at async Test.run (node:internal/test_runner/test:1404:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:969:7) {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: 'OnRamp',
    expected: 'EVM2EVMOnRamp',
    operator: 'strictEqual',
    diff: 'simple'
  }
---------------------------------------|---------|----------|---------|---------|-------------------

Summary

ℹ tests 2579
ℹ suites 661
ℹ pass 2570
ℹ fail 3
ℹ cancelled 0
ℹ skipped 6
ℹ todo 0
ℹ duration_ms 353143.631947
Coverage report
File                                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------------------------------|---------|----------|---------|---------|-------------------
All files                              |   84.41 |    82.99 |   76.96 |   84.41 |                   
 ccip-cli/src                          |   90.42 |    59.25 |      75 |   90.42 |                   
  index.ts                             |   90.42 |    59.25 |      75 |   90.42 | ...73-174,180-185 
 ccip-cli/src/commands                 |   52.71 |    75.87 |    62.9 |   52.71 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  lane-latency.ts                      |   70.76 |    83.33 |   33.33 |   70.76 | ...,76-83,119-128 
  lane.ts                              |   86.98 |    68.42 |     100 |   86.98 | ...41-144,148-150 
  manual-exec.ts                       |   20.34 |      100 |       0 |   20.34 | ...39-149,151-347 
  parse.ts                             |    55.2 |      100 |       0 |    55.2 | 47-51,58-65,67-96 
  search.ts                            |   81.25 |      100 |       0 |   81.25 | 24-29             
  send.ts                              |   14.13 |      100 |       0 |   14.13 | ...09-237,239-552 
  show.ts                              |   75.68 |    63.75 |     100 |   75.68 | ...15-318,399-401 
  supported-tokens.ts                  |   17.93 |      100 |       0 |   17.93 | ...80-353,355-368 
  token.ts                             |   22.72 |      100 |       0 |   22.72 | ...3,60-67,69-132 
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |   88.13 |     80.1 |   96.66 |   88.13 | ...11-721,729-737 
 ccip-cli/src/commands/search          |   53.89 |    76.19 |    12.5 |   53.89 |                   
  messages.ts                          |   53.89 |    76.19 |    12.5 |   53.89 | ...36-258,260-295 
 ccip-cli/src/providers                |   53.37 |    86.45 |   21.73 |   53.37 |                   
  aptos.ts                             |   51.47 |      100 |       0 |   51.47 | ...,95-96,105-136 
  evm.ts                               |    36.3 |      100 |       0 |    36.3 | ...69-104,116-168 
  index.ts                             |   81.37 |    85.71 |    90.9 |   81.37 | ...20,290,344-392 
  solana.ts                            |   47.44 |      100 |       0 |   47.44 | ...02-103,112-137 
  sui.ts                               |    49.5 |      100 |       0 |    49.5 | ...57-258,267-301 
  ton.ts                               |   15.03 |      100 |       0 |   15.03 | 24-153            
 ccip-cli/src/providers/canton         |    85.4 |    77.89 |      68 |    85.4 |                   
  auth.ts                              |   82.41 |    73.01 |   53.33 |   82.41 | ...17-333,464-469 
  config.ts                            |   80.41 |    81.81 |     100 |   80.41 | 36-43,48-58       
  index.ts                             |     100 |      100 |     100 |     100 |                   
  wallet.ts                            |   92.93 |      100 |   85.71 |   92.93 | 172-184           
 ccip-sdk/src                          |   95.45 |    88.33 |    92.9 |   95.45 |                   
  chain.ts                             |   96.52 |     84.5 |   84.37 |   96.52 | ...2427,2460-2461 
  commits.ts                           |    90.9 |    81.25 |     100 |    90.9 | ...06,115-118,120 
  execution.ts                         |   93.13 |    88.23 |     100 |   93.13 | ...53-159,192-199 
  explorer.ts                          |     100 |      100 |     100 |     100 |                   
  extra-args.ts                        |     100 |    89.47 |     100 |     100 | 225,260,297,312   
  fetch.ts                             |   98.06 |    93.56 |   97.77 |   98.06 | ...-881,1127-1129 
  gas.ts                               |   85.14 |    86.04 |      75 |   85.14 | ...82-293,402-451 
  http-status.ts                       |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  messages.ts                          |    79.2 |    47.16 |      40 |    79.2 | ...99-310,314-327 
  networks.ts                          |     100 |      100 |     100 |     100 |                   
  offchain.ts                          |   84.64 |    76.92 |     100 |   84.64 | ...18-227,236-237 
  requests.ts                          |    95.4 |    90.58 |     100 |    95.4 | ...54-655,680-685 
  supported-chains.ts                  |     100 |      100 |     100 |     100 |                   
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |    99.2 |       96 |     100 |    99.2 | ...12,217-218,220 
 ccip-sdk/src/api                      |   93.22 |    83.44 |   94.73 |   93.22 |                   
  index.ts                             |   93.22 |    83.44 |   94.73 |   93.22 | ...92-895,898-901 
 ccip-sdk/src/aptos                    |   78.76 |    78.44 |   76.19 |   78.76 |                   
  exec.ts                              |   29.31 |      100 |       0 |   29.31 | 18-58             
  hasher.ts                            |   75.89 |       80 |   66.66 |   75.89 | 17-36,50-56       
  index.ts                             |   68.84 |    79.83 |   67.44 |   68.84 | ...1072,1076-1087 
  logs.ts                              |   96.47 |    77.94 |    92.3 |   96.47 | ...1442-1444,1731 
  send.ts                              |    25.6 |      100 |       0 |    25.6 | ...2,63-80,93-125 
  token.ts                             |   23.75 |       75 |     100 |   23.75 | 35-156            
  types.ts                             |   65.97 |      100 |       0 |   65.97 | 26-33,65-89       
 ccip-sdk/src/canton                   |   45.32 |    76.16 |   31.66 |   45.32 |                   
  amount.ts                            |   92.59 |       70 |     100 |   92.59 | 22-23             
  ccv-addresses.ts                     |     100 |    91.07 |     100 |     100 | 13,30,64,67       
  defaults.ts                          |   99.28 |    90.24 |     100 |   99.28 | 93                
  events.ts                            |   45.38 |     52.5 |   23.52 |   45.38 | ...15-527,533-542 
  index.ts                             |   34.89 |    58.82 |   12.82 |   34.89 | ...2614,2617-2619 
  types.ts                             |   95.26 |      100 |      50 |   95.26 | 55-62             
  update-id.ts                         |   98.11 |    88.88 |     100 |   98.11 | 52                
 ccip-sdk/src/canton/authentication    |   93.05 |    79.31 |    92.3 |   93.05 |                   
  authorization-code.ts                |   89.83 |    84.31 |    90.9 |   89.83 | ...58-376,432-443 
  client-credentials.ts                |   91.28 |       75 |     100 |   91.28 | ...92-196,198-202 
  index.ts                             |    96.8 |       75 |     100 |    96.8 | 168-173           
  metadata.ts                          |   81.89 |    44.44 |      50 |   81.89 | ...55,73-78,93-94 
  static.ts                            |     100 |      100 |     100 |     100 |                   
  token-source.ts                      |   98.88 |    77.77 |     100 |   98.88 | 165-166           
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/canton/client            |    57.4 |    60.86 |   22.85 |    57.4 |                   
  client.ts                            |   55.62 |    59.09 |   22.85 |   55.62 | ...37-756,771-791 
  index.ts                             |     100 |      100 |     100 |     100 |                   
 ...dk/src/canton/explicit-disclosures |   74.81 |    69.35 |    55.1 |   74.81 |                   
  acs.ts                               |   87.36 |     69.1 |      90 |   87.36 | ...20,568,606-607 
  eds.ts                               |   54.34 |      100 |       0 |   54.34 | ...86-393,397-402 
 ccip-sdk/src/canton/token-metadata    |   61.37 |      100 |       0 |   61.37 |                   
  client.ts                            |   61.37 |      100 |       0 |   61.37 | 111-178,185-189   
 ...dk/src/canton/transfer-instruction |    52.4 |      100 |       0 |    52.4 |                   
  client.ts                            |    52.4 |      100 |       0 |    52.4 | 104-197,204-208   
 ccip-sdk/src/cct                      |     100 |    97.43 |     100 |     100 |                   
  errors.ts                            |     100 |    96.87 |     100 |     100 | 218               
  operation.ts                         |     100 |      100 |     100 |     100 |                   
  query.ts                             |     100 |      100 |     100 |     100 |                   
  token-manager.ts                     |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm                  |   96.63 |    96.09 |   61.64 |   96.63 |                   
  index.ts                             |   95.37 |      100 |      44 |   95.37 | ...1381,1413-1416 
  operation.ts                         |     100 |      100 |     100 |     100 |                   
  query.ts                             |     100 |      100 |     100 |     100 |                   
  submit.ts                            |     100 |    79.16 |     100 |     100 | 28,52,68-69,87    
  validate.ts                          |     100 |      100 |     100 |     100 |                   
 .../cct/evm/artifacts/bytecode/V2_0_0 |     100 |      100 |     100 |     100 |                   
  burn-from-mint-token-pool.ts         |     100 |      100 |     100 |     100 |                   
  burn-mint-token-pool.ts              |     100 |      100 |     100 |     100 |                   
  burn-with-from-mint-token-pool.ts    |     100 |      100 |     100 |     100 |                   
  cross-chain-token.ts                 |     100 |      100 |     100 |     100 |                   
  erc20-lockbox.ts                     |     100 |      100 |     100 |     100 |                   
  lock-release-token-pool.ts           |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm/lockbox          |     100 |      100 |     100 |     100 |                   
  contracts.ts                         |     100 |      100 |     100 |     100 |                   
 ...sdk/src/cct/evm/lockbox/operations |     100 |      100 |     100 |     100 |                   
  authorize-callers.ts                 |     100 |      100 |     100 |     100 |                   
  deploy-lockbox.ts                    |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm/token            |   98.93 |     62.5 |   83.33 |   98.93 |                   
  contracts.ts                         |   98.93 |     62.5 |   83.33 |   98.93 | 51-52             
 ...k/src/cct/evm/token-admin-registry |     100 |      100 |     100 |     100 |                   
  contracts.ts                         |     100 |      100 |     100 |     100 |                   
 ...vm/token-admin-registry/operations |     100 |      100 |     100 |     100 |                   
  accept-admin.ts                      |     100 |      100 |     100 |     100 |                   
  get-supported-tokens.ts              |     100 |      100 |     100 |     100 |                   
  get-token-admin-registry.ts          |     100 |      100 |     100 |     100 |                   
  register-admin.ts                    |     100 |      100 |     100 |     100 |                   
  set-pool.ts                          |     100 |      100 |     100 |     100 |                   
  transfer-admin.ts                    |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm/token-pool       |     100 |      100 |     100 |     100 |                   
  contracts.ts                         |     100 |      100 |     100 |     100 |                   
  rate-limit.ts                        |     100 |      100 |     100 |     100 |                   
  remote-pool.ts                       |     100 |      100 |     100 |     100 |                   
 .../src/cct/evm/token-pool/operations |   99.82 |    97.57 |     100 |   99.82 |                   
  add-remote-pool.ts                   |     100 |      100 |     100 |     100 |                   
  apply-allowlist-updates.ts           |     100 |    96.29 |     100 |     100 | 229               
  apply-chain-updates.ts               |     100 |      100 |     100 |     100 |                   
  deploy-token-pool.ts                 |     100 |    85.71 |     100 |     100 | 83-86             
  get-token-pool-remotes.ts            |     100 |      100 |     100 |     100 |                   
  get-token-pool-state.ts              |   98.61 |       95 |     100 |   98.61 | 282-285           
  remove-remote-pool.ts                |     100 |      100 |     100 |     100 |                   
  set-chain-rate-limiter-configs.ts    |     100 |    97.05 |     100 |     100 | 112               
  set-dynamic-config.ts                |     100 |      100 |     100 |     100 |                   
  set-rate-limit-admin.ts              |     100 |      100 |     100 |     100 |                   
  set-remote-pool.ts                   |     100 |      100 |     100 |     100 |                   
  transfer-ownership.ts                |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm/token/operations |     100 |      100 |     100 |     100 |                   
  deploy-token.ts                      |     100 |      100 |     100 |     100 |                   
  get-burners.ts                       |     100 |      100 |     100 |     100 |                   
  get-minters.ts                       |     100 |      100 |     100 |     100 |                   
  is-burner.ts                         |     100 |      100 |     100 |     100 |                   
  is-minter.ts                         |     100 |      100 |     100 |     100 |                   
  mint.ts                              |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/solana               |   99.53 |    96.22 |   98.11 |   99.53 |                   
  index.ts                             |   99.67 |      100 |   97.22 |   99.67 | 258-259,2425-2430 
  operation.ts                         |     100 |      100 |     100 |     100 |                   
  query.ts                             |     100 |      100 |     100 |     100 |                   
  serialize.ts                         |     100 |     92.3 |     100 |     100 | 27                
  submit.ts                            |   96.95 |    86.27 |     100 |   96.95 | 58-59,106-110     
  validate.ts                          |     100 |    98.34 |     100 |     100 | 284,391           
 ccip-sdk/src/cct/solana/programs      |     100 |      100 |     100 |     100 |                   
  alt.ts                               |     100 |      100 |     100 |     100 |                   
  fee-quoter.ts                        |     100 |      100 |     100 |     100 |                   
  router.ts                            |     100 |      100 |     100 |     100 |                   
  token-pool.ts                        |     100 |      100 |     100 |     100 |                   
  token.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/solana/token         |     100 |      100 |     100 |     100 |                   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
 ...rc/cct/solana/token-admin-registry |     100 |      100 |     100 |     100 |                   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
 ...na/token-admin-registry/operations |   99.61 |    98.75 |     100 |   99.61 |                   
  accept-admin.ts                      |     100 |      100 |     100 |     100 |                   
  append-to-lookup-table.ts            |     100 |      100 |     100 |     100 |                   
  create-lookup-table.ts               |     100 |      100 |     100 |     100 |                   
  get-supported-tokens.ts              |     100 |      100 |     100 |     100 |                   
  get-token-admin-registry.ts          |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  ...override-pending-administrator.ts |     100 |      100 |     100 |     100 |                   
  register-admin.ts                    |   97.95 |    94.44 |     100 |   97.95 | 212-216           
  set-pool.ts                          |     100 |      100 |     100 |     100 |                   
  transfer-admin.ts                    |     100 |      100 |     100 |     100 |                   
 ...c/cct/solana/token-pool/operations |   99.63 |    98.49 |     100 |   99.63 |                   
  accept-ownership.ts                  |     100 |      100 |     100 |     100 |                   
  append-remote-pool-addresses.ts      |     100 |      100 |     100 |     100 |                   
  apply-chain-updates.ts               |     100 |    98.43 |     100 |     100 | 149               
  configure-allowlist.ts               |     100 |      100 |     100 |     100 |                   
  create-token-multisig.ts             |    95.9 |     92.5 |     100 |    95.9 | ...01-102,104-109 
  delete-chain-remote-config.ts        |     100 |      100 |     100 |     100 |                   
  deploy-token-pool.ts                 |     100 |      100 |     100 |     100 |                   
  edit-chain-remote-config.ts          |     100 |      100 |     100 |     100 |                   
  get-token-pool-remotes.ts            |     100 |      100 |     100 |     100 |                   
  get-token-pool-state.ts              |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  init-chain-remote-config.ts          |     100 |      100 |     100 |     100 |                   
  provide-liquidity.ts                 |     100 |      100 |     100 |     100 |                   
  remove-from-allowlist.ts             |     100 |      100 |     100 |     100 |                   
  set-can-accept-liquidity.ts          |     100 |      100 |     100 |     100 |                   
  set-chain-rate-limit.ts              |   99.11 |    96.15 |     100 |   99.11 | 64-65             
  set-rate-limit-admin.ts              |     100 |      100 |     100 |     100 |                   
  set-rebalancer.ts                    |     100 |      100 |     100 |     100 |                   
  transfer-ownership.ts                |     100 |      100 |     100 |     100 |                   
  withdraw-liquidity.ts                |     100 |      100 |     100 |     100 |                   
 ...dk/src/cct/solana/token/operations |     100 |    97.97 |     100 |     100 |                   
  approve-token.ts                     |     100 |      100 |     100 |     100 |                   
  create-token-account.ts              |     100 |      100 |     100 |     100 |                   
  deploy-token.ts                      |     100 |      100 |     100 |     100 |                   
  get-token-info.ts                    |     100 |       70 |     100 |     100 | 69,80-81          
  index.ts                             |     100 |      100 |     100 |     100 |                   
  mint-tokens.ts                       |     100 |      100 |     100 |     100 |                   
  set-token-authority.ts               |     100 |    97.61 |     100 |     100 | 139               
  update-metadata-authority.ts         |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/errors                   |   90.42 |    77.16 |   59.55 |   90.42 |                   
  CCIPError.ts                         |     100 |      100 |     100 |     100 |                   
  codes.ts                             |     100 |      100 |     100 |     100 |                   
  index.ts                             |     100 |      100 |     100 |     100 |                   
  pure.ts                              |     100 |       75 |     100 |     100 | 32                
  recovery.ts                          |     100 |      100 |     100 |     100 |                   
  specialized.ts                       |   88.26 |    73.91 |   57.36 |   88.26 | ...3636,3659-3668 
  utils.ts                             |   94.44 |    81.48 |     100 |   94.44 | 15,17,22,24       
 ccip-sdk/src/evm                      |   92.88 |    82.45 |   93.16 |   92.88 |                   
  const.ts                             |   98.66 |    92.85 |     100 |   98.66 | 122-123           
  errors.ts                            |   91.98 |    81.57 |     100 |   91.98 | ...85,239-242,247 
  extra-args.ts                        |    94.5 |    61.01 |     100 |    94.5 | ...11-212,328-340 
  fork.test.data.ts                    |     100 |      100 |     100 |     100 |                   
  gas.ts                               |   98.19 |    63.15 |     100 |   98.19 | 90-91,93          
  hasher.ts                            |     100 |     92.3 |     100 |     100 | 135               
  index.ts                             |   88.32 |    80.61 |    90.9 |   88.32 | ...2716,2871-2902 
  logs.ts                              |   99.16 |    92.02 |      90 |   99.16 | 69-70,75-76       
  messageCodec.ts                      |     100 |      100 |     100 |     100 |                   
  messages.ts                          |     100 |      100 |     100 |     100 |                   
  offchain.ts                          |    87.5 |    71.42 |     100 |    87.5 | 13-14             
  simulate.ts                          |     100 |    94.82 |     100 |     100 | 109-111,425       
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/evm/viem                 |   79.76 |    90.62 |   69.23 |   79.76 |                   
  client-adapter.ts                    |     100 |       90 |     100 |     100 | 48,74             
  index.ts                             |     100 |      100 |     100 |     100 |                   
  wallet-adapter.ts                    |   63.09 |     90.9 |   55.55 |   63.09 | ...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 | ...06-307,315-316 
 ccip-sdk/src/shared                   |   85.32 |    84.33 |   82.35 |   85.32 |                   
  bcs-codecs.ts                        |   79.07 |    60.86 |   66.66 |   79.07 | ...42-252,260-269 
  codec.ts                             |    94.3 |    93.22 |     100 |    94.3 | 139-140,178-186   
  constants.ts                         |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/solana                   |   79.07 |    75.11 |   84.34 |   79.07 |                   
  cleanup.ts                           |   26.95 |    66.66 |   33.33 |   26.95 | ...59-101,114-227 
  exec.ts                              |   69.11 |    62.96 |   66.66 |   69.11 | ...69-473,513-514 
  extra-args.ts                        |   59.49 |    76.47 |      60 |   59.49 | 86-122,132-190    
  fork.test.data.ts                    |     100 |      100 |     100 |     100 |                   
  gas.ts                               |   92.06 |    68.57 |     100 |   92.06 | ...,81-90,170-171 
  hasher.ts                            |   96.49 |    81.81 |     100 |   96.49 | 64-67             
  index.ts                             |   83.03 |    78.91 |   87.03 |   83.03 | ...2129,2133-2166 
  logs.ts                              |   87.79 |    74.07 |     100 |   87.79 | ...50-151,163-164 
  offchain.ts                          |     100 |      100 |     100 |     100 |                   
  patchBorsh.ts                        |   78.31 |       50 |     100 |   78.31 | ...47,65-66,72-78 
  send.ts                              |    77.6 |    33.33 |      80 |    77.6 | ...52-360,403-442 
  signatures-cache.ts                  |   79.78 |    71.15 |    87.5 |   79.78 | ...51-260,274-275 
  token-admin-registry.ts              |     100 |    96.29 |     100 |     100 | 27                
  types.ts                             |     100 |      100 |     100 |     100 |                   
  utils.ts                             |   81.09 |    71.62 |   83.33 |   81.09 | ...91-593,625-640 
 ccip-sdk/src/sui                      |   73.18 |    70.62 |   81.14 |   73.18 |                   
  discovery.ts                         |   47.37 |    48.27 |      60 |   47.37 | ...34-782,785-819 
  events.ts                            |   89.83 |    72.67 |     100 |   89.83 | ...19-626,685-698 
  exec.ts                              |   30.71 |      100 |       0 |   30.71 | 37-89,101-153     
  hasher.ts                            |   98.19 |    66.66 |     100 |   98.19 | 35,51             
  index.ts                             |   76.12 |    70.39 |   79.68 |   76.12 | ...2314,2330-2331 
  logs.ts                              |   97.89 |    74.02 |     100 |   97.89 | ...62-165,186-187 
  objects.ts                           |    54.6 |    83.33 |   83.33 |    54.6 | ...57-313,324-467 
  types.ts                             |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/sui/manuallyExec         |   53.36 |       75 |      50 |   53.36 |                   
  encoder.ts                           |    83.9 |    66.66 |     100 |    83.9 | 51-58,73-77,82    
  index.ts                             |   33.82 |      100 |       0 |   33.82 | 47-136            
 ccip-sdk/src/ton                      |      86 |    80.53 |   88.28 |      86 |                   
  exec.ts                              |     100 |      100 |     100 |     100 |                   
  extra-args.ts                        |   98.66 |    72.72 |     100 |   98.66 | 156-157,222       
  hasher.ts                            |   78.07 |    77.77 |      75 |   78.07 | 100-108,156-187   
  index.ts                             |   84.81 |     79.6 |   79.03 |   84.81 | ...2111,2118-2119 
  logs.ts                              |   93.84 |    83.43 |     100 |   93.84 | ...09-616,672-675 
  send.ts                              |   95.52 |    66.66 |     100 |   95.52 | 37-44,188         
  ton-cache.ts                         |     100 |    96.55 |     100 |     100 | 102               
  types.ts                             |   91.24 |    81.25 |     100 |   91.24 | ...61-63,71-74,92 
  utils.ts                             |    63.1 |    77.77 |    90.9 |    63.1 | ...37-395,397-400 
 scripts                               |   87.95 |    63.88 |     100 |   87.95 |                   
  test-endpoints.ts                    |   97.98 |    72.72 |     100 |   97.98 | 137-139           
  useResource.ts                       |   83.57 |    62.29 |     100 |   83.57 | ...81-286,301-302 
---------------------------------------|---------|----------|---------|---------|-------------------

@aelmanaa

aelmanaa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

The Bug & The Fix

The Bug

ccip-sdk/src/solana/utils.ts simulateAndSendTxs splits an instruction list that is too large by catching a simulation failure and dropping the last instruction, then retrying. The catch does not distinguish why simulation failed. A program rejection — for example SPL Token OwnerMismatch — is treated the same as an over-size/over-budget failure: the failing instruction is dropped and the valid prefix is signed and committed as its own transaction.

Concrete case: a token has wallet A as mint authority and wallet B as freeze authority. A calls setTokenAuthority({ authorityTypes: ['mint', 'freeze'], newAuthority: C }). The combined transaction fails simulation on the freeze instruction with OwnerMismatch. The old code drops the freeze instruction, commits the mint-authority change (A → C), retries the freeze update, fails, and returns an error — leaving the mint authority already changed while telling the caller the operation failed.

The Fix

One shared send routine (sendInstructionSlices) replaces the truncate-on-any-error loop and is used by both the CCT ops and the primary send path.

File Change
solana/utils.ts New sendInstructionSlices: (1) shrink a slice by serialized size before simulating; (2) on a simulation failure, split only if it is a compute-budget error, otherwise rethrow; (3) never truncate on a program rejection. simulateAndSendTxs now delegates to it, so ccipSend and cleanup get the fix too.
cct/solana/submit.ts Removes the duplicated send loop; submit calls sendInstructionSlices with requireSingleTransaction, and still reports committedHashes on failure.
errors/{codes,recovery,specialized,index}.ts Adds CCIPSolanaTransactionTooLargeError, thrown when a single instruction does not fit or an atomic op is too large to split (replaces a raw throw new Error).
cct/solana/submit.test.ts Two unit tests: a compute-budget error splits into two txs; a program rejection on a later instruction is not truncated and commits nothing.
cct/solana/token/operations/set-token-authority.integration.test.ts (new) Integration test on Surfpool (local validator, real SPL Token): the on-chain VM rolls both authority changes back, and setTokenAuthority rejects without changing the mint authority.

Compute-budget vs program failure is decided on the structured Solana error, not on message text:

function isComputeBudgetError(error: unknown): boolean {
 if (!(error instanceof SendTransactionError)) return false
 const structured = (error as { simulationError?: unknown }).simulationError
 if (typeof structured === 'string') return COMPUTE_BUDGET_ERRORS.has(structured)
 if (structured && typeof structured === 'object' && 'InstructionError' in structured) {
   const detail = (structured as { InstructionError?: unknown }).InstructionError
   return Array.isArray(detail) && typeof detail[1] === 'string' && COMPUTE_BUDGET_ERRORS.has(detail[1])
 }
 return false
}

The split-vs-rethrow decision in the slice loop:

try {
 unitsConsumed = (await simulateTransaction(ctx, { payerKey, instructions: slice.instructions, ... })).unitsConsumed || 0
 break
} catch (error) {
 // split only on a compute-budget (resource) error; rethrow every program rejection
 if (!requireSingleTransaction && isComputeBudgetError(error) && end - 1 > start) {
   slice = getInstructionSlice(unsigned, start, --end)
   continue
 }
 throw error
}

Properties the Fix Guarantees

Property How
Truncate only for size and compute failures; rethrow everything else Size shrink runs before simulation; compute split is gated on the structured error; all program rejections rethrow
Classify by structured error type, not message text Classifier reads simulationError (the Solana TransactionError), not logs or the message
Validate fit before submission exceedsTransactionSize size probe runs before each send
setTokenAuthority mint+freeze with a failing freeze commits nothing Authority pre-check rejects before submit; the submit path also rethrows the program error without truncating
Fix covers the primary send path The fix is in simulateAndSendTxs, so ccipSend and cleanup are covered
Atomic operations stay atomic setTokenAuthority passes requireSingleTransaction: true
Size splitting still works Proven on devnet (below)

Proof

P1 — The bug is real (devnet)

The old truncating loop, given [setMint (valid), setFreeze (fails OwnerMismatch)], committed the mint-authority change to the new key and then returned an error. Evidence: after the run, the mint's on-chain authority had changed to the new key (partial commit). This is the defect being fixed.

P2 — The fix prevents the partial commit at the send path (devnet)

The same [setMint (valid), setFreeze (fails)] sent directly through the fixed simulateAndSendTxs (no authority pre-check involved) throws and commits nothing. Evidence: the mint's on-chain authority is unchanged after the run. (No success transaction exists to link — the observable is the mint state.)

P3 — setTokenAuthority is atomic (devnet)

setTokenAuthority({ authorityTypes: ['mint','freeze'], newAuthority: C }) changes both authorities in one transaction. Evidence: tx 4T11U1qt…95jX; both mintAuthority and freezeAuthority read as C on-chain after that single transaction.

P4 — The primary send path (ccipSend) still works (devnet)

A single-instruction send through simulateAndSendTxs sends and confirms. Evidence: tx 4jhMS4uU…ukuHX.

P5 — Size splitting still works (devnet)

8 memo instructions (~2000 bytes, over the 1232-byte limit) split into two confirmed transactions, no truncation. Evidence: tx 2ykDP9Fu…w9vb and tx 3xAY59Tn…uif3.

P6 — On-chain atomicity holds independent of the SDK (Surfpool integration test, control case)

A single transaction with both authority changes fails on the freeze instruction (OwnerMismatch) and the validator rolls both changes back. Evidence: set-token-authority.integration.test.ts control case passes on Surfpool.

P7 — setTokenAuthority commits nothing on a failed combined update (Surfpool integration test, regression case)

Evidence: set-token-authority.integration.test.ts regression case — after the rejected call, the mint authority is unchanged.

P8 — A compute-budget failure splits, a program rejection does not (unit)

Evidence: submit.test.ts — one test drives a ComputationalBudgetExceeded simulation error and asserts two transactions are sent; another drives a program rejection on a later instruction and asserts zero transactions are sent. A live >1.4M-compute-unit operation cannot be constructed on-chain, so the compute-split path is proven by unit test only.

P9 — Nothing regresses; CI is green

Evidence: npm ci exits 0 (install + build); tsc, oxfmt, oxlint clean on all changed files; 402 Solana/CCT unit tests, 101 error/submit tests, and 2 Surfpool integration tests all pass.


Grounding

  • PACKET_DATA_SIZE = 1232 bytes is the Solana serialized-transaction limit; an oversized transaction fails to serialize (encoding overruns Uint8Array).
  • SPL set_authority changes one authority per instruction and requires the current authority to sign, so a wrong authority reverts OwnerMismatch. The pre-check reads the mint and rejects a mismatch before building the transaction, so the same rejection happens off-chain instead of on-chain. Reassign and revoke (null) are both handled.
  • Blast radius: 30 submit(...) call sites, all on the 5-arg signature (the 6th requireSingleTransaction is optional); only setTokenAuthority opts into true. Single-instruction ops are unchanged; multi-instruction ops were never atomic before either (they split by size), so the default preserves prior behavior.

@aelmanaa

aelmanaa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Finding #1 — 🟡 Medium (DX): Partial-Failure State is Hard to Act On

On a split failure, committedHashes (which transactions already landed) is only documented on the internal submit JSDoc, not on CCTTxFailedError/CCTTxNotConfirmedError, and the message does not say the state is partially changed.

Impact: Callers receiving an error don't know whether partial state was committed and cannot reason about recovery (retry, rollback, etc.).

Fix:

  1. Document context.committedHashes on both error classes (location: cct/errors.ts)
  2. Prepend "partially applied: N transaction(s) confirmed" to the reason when committedHashes is present (location: submit.ts)

Example:

  • Before: Error: simulation failed
  • After: Error: partially applied: 1 transaction(s) confirmed; simulation failed

This change makes it clear to operators that prior slices succeeded and are permanent.

@aelmanaa

aelmanaa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Finding #2 — 🟡 Medium (DX): Authority Pre-Check Error Message Lacks Context

The authority pre-check error does not name the current authority: "must match the token mint authority" does not say what the correct value is, and the already-revoked (null) case shows the same text.

Impact: When validation fails, callers don't see the on-chain authority value and must make a separate query to understand what went wrong.

Fix: Enhance error message with the actual value (location: set-token-authority.ts:143-153):

  • Current: "must match the token mint authority"
  • Suggested: "authority ${provided} is not the current ${type} authority (${current ?? 'already revoked'})"

Alternatively: Put currentAuthority in the error context object for programmatic access.

Example:

  • Before: Error: authority must match the token mint authority
  • After: Error: authority ABC... is not the current mint authority (XYZ...)

This reduces the round-trip to understand validation failures.

@aelmanaa

aelmanaa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Finding #3 — 🟡 Low: Missing @throws Documentation on execute()

The class docs cover the atomicity guarantee, but the public execute facade has only a one-line doc.

Fix: Add @throws for all error types thrown by this operation (location: set-token-authority.ts:176-208):

  • CCTParamsInvalidError (invalid authority types, mismatched authority, multisig on execute)
  • CCIPTokenDataParseError (malformed mint data)
  • CCTTxFailedError (on-chain program rejection, e.g., wrong authority)
  • CCTTxNotConfirmedError (blockhash expired, not confirmed, etc.)

Example:

/**
 * Generate, sign, simulate, send, and confirm with the current authority wallet.
 * 
 * @throws {CCTParamsInvalidError} If authority types are invalid or if authority does not match the executing wallet
 * @throws {CCIPTokenDataParseError} If the mint data cannot be parsed
 * @throws {CCTTxFailedError} If the transaction fails on-chain (e.g., wrong current authority)
 * @throws {CCTTxNotConfirmedError} If the transaction is not confirmed in time
 */
override async execute(...)

Finding #4 — Nits (Polish)

(a) Rename the concept from "truncation"

An oversized transaction hard-fails at serialize time, it is not silently truncated. "Size overflow" is more accurate.

(b) Clarify the atomicity wiring

The atomicity is wired by a bare positional true in the submit(...) call. Add an inline comment:

return submit(..., /* requireSingleTransaction */ true)

(c) Document ALT-only-on-main-slice behavior

The lookup-tables-only-on-the-main-slice behavior silently drops ALTs on non-main slices. Safe today (no CCT op sets lookupTables), but add a comment/assert so a future ALT-bearing op keeps ALT-referencing instructions on the main slice.

(d) Namespace committedHashes to avoid key collisions

apply-chain-updates.ts does Object.assign(error.context, {committedHashes}), which could overwrite the committedHashes the submit path sets — moot today (batches are packed to one tx), but namespace it to avoid a latent key collision.

@aelmanaa

aelmanaa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Recommendation & Summary

APPROVE. The bug is reproduced (P1), the fix prevents it (P2), and both are proven on a local validator and on devnet. The remaining items are documentation/DX polish (findings above), not blockers.

What ships:

  1. ✅ Atomicity guarantee for setTokenAuthority (mint + freeze in one tx, or none)
  2. ✅ Program rejection rethrow (no truncation on OwnerMismatch, etc.)
  3. ✅ Size-based splitting still works (deterministic PACKET_DATA_SIZE check)
  4. ✅ All proofs validated (9 scenarios: Devnet txs + integration tests + unit tests)
  5. ✅ No regressions (402 Solana/CCT + 101 error/submit + 2 Surfpool integration tests pass)

Polish items (non-blocking):

Recommendation: Ship. Polish can follow in a focused commit if preferred.

@mervin-link

Copy link
Copy Markdown
Collaborator Author

Recommendation & Summary

APPROVE. The bug is reproduced (P1), the fix prevents it (P2), and both are proven on a local validator and on devnet. The remaining items are documentation/DX polish (findings above), not blockers.

What ships:

  1. ✅ Atomicity guarantee for setTokenAuthority (mint + freeze in one tx, or none)
  2. ✅ Program rejection rethrow (no truncation on OwnerMismatch, etc.)
  3. ✅ Size-based splitting still works (deterministic PACKET_DATA_SIZE check)
  4. ✅ All proofs validated (9 scenarios: Devnet txs + integration tests + unit tests)
  5. ✅ No regressions (402 Solana/CCT + 101 error/submit + 2 Surfpool integration tests pass)

Polish items (non-blocking):

Recommendation: Ship. Polish can follow in a focused commit if preferred.

@aelmanaa Addressed all comments.

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.

2 participants