Skip to content

feat(cct-sdk): Add CrossChainToken admin operations for EVM - #432

Open
mervin-link wants to merge 2 commits into
cct-sdkfrom
feat/DAPP-11562-default-admin-transfer
Open

mervin-link wants to merge 2 commits into
cct-sdkfrom
feat/DAPP-11562-default-admin-transfer

Conversation

@mervin-link

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

Copy link
Copy Markdown
Collaborator

What

  • DAPP-11562
  • Add v2 CrossChainToken default-admin transfer begin, accept, and cancel operations
  • Add v2 setCCIPAdmin operation
  • Add operation tests, validation, and SDK facade documentation

Why

  • Enable EVM SDK users to manage CrossChainToken default-admin transfers and the separate CCIP admin.

Notes

  • Operations are v2 CrossChainToken only.
  • setCCIPAdmin(0x0) is supported by CrossChainToken, unlike BaseERC20.

@mervin-link
mervin-link requested a review from a team as a code owner September 22, 2026 07:29
@vercel

vercel Bot commented Sep 22, 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 22, 2026

Copy link
Copy Markdown

CI Test Report

3 of 3049 tests did not pass (pass 3040, fail 3, cancelled 0, skipped 6) in 5m 42s

Failed suites: ccip-sdk/src/evm/dest-liquidity.integration.test.ts, ccip-sdk/src/evm/integration.test.ts

Failed tests
test at ccip-sdk/src/evm/dest-liquidity.integration.test.ts:153:7
✖ LBTC dest on ethereum (LombardTokenPoolV2) => attestation-required pre-send, not a block (104.212825ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
  + actual - expected
  
  + 'LombardTokenPool'
  - 'LombardTokenPoolV2'
                     ^
  
      at TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/evm/dest-liquidity.integration.test.ts:159:16)
      at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: 'LombardTokenPool',
    expected: 'LombardTokenPoolV2',
    operator: 'strictEqual',
    diff: 'simple'
  }

test at ccip-sdk/src/evm/dest-liquidity.integration.test.ts:153:7
✖ LBTC dest on monad (LombardTokenPoolV2) => attestation-required pre-send, not a block (120.696927ms)
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
  + actual - expected
  
  + 'LombardTokenPool'
  - 'LombardTokenPoolV2'
                     ^
  
      at TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/evm/dest-liquidity.integration.test.ts:159:16)
      at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: 'LombardTokenPool',
    expected: 'LombardTokenPoolV2',
    operator: 'strictEqual',
    diff: 'simple'
  }

test at ccip-sdk/src/evm/integration.test.ts:701:5
✖ should return FINALITY_FAST=undefined and no rate limits for v1.6 lane (50.681458ms)
  AssertionError [ERR_ASSERTION]: v1.6 lane should not include FINALITY_FAST (FTF does not exist pre-v2.0)
  
  1 !== undefined
  
      at TestContext.<anonymous> (file:///home/runner/work/ccip-tools-ts/ccip-tools-ts/ccip-sdk/src/evm/integration.test.ts:709:14)
      at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
      at async Test.run (node:internal/test_runner/test:1409:7)
      at async Promise.all (index 0)
      at async Suite.run (node:internal/test_runner/test:1905:7)
      at async Suite.processPendingSubtests (node:internal/test_runner/test:974:7) {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: 1,
    expected: undefined,
    operator: 'strictEqual',
    diff: 'simple'
  }
---------------------------------------|---------|----------|---------|---------|-------------------

Summary

ℹ tests 3049
ℹ suites 809
ℹ pass 3040
ℹ fail 3
ℹ cancelled 0
ℹ skipped 6
ℹ todo 0
ℹ duration_ms 341729.370543
Coverage report
File                                   | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------------------------------|---------|----------|---------|---------|-------------------
All files                              |   85.58 |    83.61 |   77.59 |   85.58 |                   
 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.47 |    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 |    79.48 |   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.41 |    86.45 |   21.73 |   53.41 |                   
  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.82 |      100 |       0 |   47.82 | ...03-104,113-138 
  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.55 |    88.62 |   93.15 |   95.55 |                   
  chain.ts                             |   96.54 |    84.36 |   84.37 |   96.54 | ...2440,2473-2474 
  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.74 |     100 |     100 | 225,260,297,312   
  fetch.ts                             |   98.33 |     93.9 |   97.87 |   98.33 | ...-942,1188-1190 
  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.28 |    96.27 |     100 |   99.28 | ...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.9 |    78.51 |   76.19 |    78.9 |                   
  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                             |   69.45 |    80.15 |   67.44 |   69.45 | ...1094,1098-1109 
  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 |      100 |     100 |     100 |                   
  errors.ts                            |     100 |      100 |     100 |     100 |                   
  operation.ts                         |     100 |      100 |     100 |     100 |                   
  query.ts                             |     100 |      100 |     100 |     100 |                   
  token-manager.ts                     |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm                  |   94.91 |    95.83 |    47.5 |   94.91 |                   
  index.ts                             |   94.06 |      100 |   33.68 |   94.06 | ...2764,2796-2799 
  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                          |   98.11 |    98.07 |     100 |   98.11 | 95-99             
 .../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.76 |    78.72 |     100 |   98.76 |                   
  contracts.ts                         |   98.76 |    78.72 |     100 |   98.76 | 76-77,99,315-316  
 ...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 |    99.06 |     100 |     100 |                   
  contracts.ts                         |     100 |    98.59 |     100 |     100 | 513               
  rate-limit.ts                        |     100 |      100 |     100 |     100 |                   
  remote-pool.ts                       |     100 |      100 |     100 |     100 |                   
 .../src/cct/evm/token-pool/operations |   99.67 |    96.28 |     100 |   99.67 |                   
  accept-pool-ownership.ts             |     100 |      100 |     100 |     100 |                   
  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 |                   
  ...en-transfer-fee-config-updates.ts |   98.17 |     93.1 |     100 |   98.17 | 160-163           
  deploy-token-pool.ts                 |     100 |    85.71 |     100 |     100 | 83-86             
  get-allowed-finality-config.ts       |     100 |      100 |     100 |     100 |                   
  get-dynamic-config.ts                |     100 |      100 |     100 |     100 |                   
  get-fee.ts                           |     100 |     90.9 |     100 |     100 | 56                
  get-rebalancer.ts                    |     100 |      100 |     100 |     100 |                   
  get-token-pool-remotes.ts            |     100 |      100 |     100 |     100 |                   
  get-token-pool-state.ts              |   98.61 |       95 |     100 |   98.61 | 282-285           
  get-token-transfer-fee-config.ts     |   94.79 |    57.14 |     100 |   94.79 | 63,66-69          
  provide-liquidity.ts                 |     100 |      100 |     100 |     100 |                   
  remove-remote-pool.ts                |     100 |      100 |     100 |     100 |                   
  set-allowed-finality-config.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-rebalancer.ts                    |     100 |      100 |     100 |     100 |                   
  set-remote-pool.ts                   |     100 |      100 |     100 |     100 |                   
  transfer-liquidity.ts                |     100 |      100 |     100 |     100 |                   
  transfer-pool-ownership.ts           |     100 |      100 |     100 |     100 |                   
  withdraw-fee-tokens.ts               |     100 |      100 |     100 |     100 |                   
  withdraw-liquidity.ts                |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/evm/token/operations |     100 |      100 |     100 |     100 |                   
  accept-default-admin-transfer.ts     |     100 |      100 |     100 |     100 |                   
  accept-token-ownership.ts            |     100 |      100 |     100 |     100 |                   
  approve-token.ts                     |     100 |      100 |     100 |     100 |                   
  begin-default-admin-transfer.ts      |     100 |      100 |     100 |     100 |                   
  cancel-default-admin-transfer.ts     |     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 |                   
  grant-burn-role.ts                   |     100 |      100 |     100 |     100 |                   
  grant-mint-and-burn-roles.ts         |     100 |      100 |     100 |     100 |                   
  grant-mint-role.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 |                   
  revoke-burn-role.ts                  |     100 |      100 |     100 |     100 |                   
  revoke-mint-role.ts                  |     100 |      100 |     100 |     100 |                   
  set-ccip-admin.ts                    |     100 |      100 |     100 |     100 |                   
  transfer-token-ownership.ts          |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/cct/solana               |   99.74 |    97.08 |   98.03 |   99.74 |                   
  index.ts                             |   99.67 |      100 |   97.22 |   99.67 | 258-259,2428-2433 
  operation.ts                         |     100 |      100 |     100 |     100 |                   
  query.ts                             |     100 |      100 |     100 |     100 |                   
  serialize.ts                         |     100 |     92.3 |     100 |     100 | 27                
  submit.ts                            |     100 |    81.81 |     100 |     100 | 31,48,53,72       
  validate.ts                          |     100 |     98.4 |     100 |     100 | 288,395           
 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 |    98.39 |     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 |      100 |     100 |     100 |                   
  update-metadata-authority.ts         |     100 |      100 |     100 |     100 |                   
 ccip-sdk/src/errors                   |    90.5 |    77.39 |   59.85 |    90.5 |                   
  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.34 |    74.24 |   57.69 |   88.34 | ...3664,3687-3696 
  utils.ts                             |   94.44 |    81.48 |     100 |   94.44 | 15,17,22,24       
 ccip-sdk/src/evm                      |   93.09 |    82.21 |   93.16 |   93.09 |                   
  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.72 |     80.2 |    90.9 |   88.72 | ...2725,2880-2911 
  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                   |   82.89 |    76.12 |    87.8 |   82.89 |                   
  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                             |   87.23 |     77.3 |   90.74 |   87.23 | ...2129,2133-2166 
  logs.ts                              |   90.11 |     80.7 |     100 |   90.11 | ...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                              |    95.7 |    53.48 |     100 |    95.7 | ...03,346-347,421 
  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                             |   82.28 |    76.92 |   85.71 |   82.28 | ...91,661,733-744 
  v1.ts                                |   94.17 |    83.72 |     100 |   94.17 | ...02-105,186-189 
 ccip-sdk/src/sui                      |   73.26 |    70.74 |   81.14 |   73.26 |                   
  discovery.ts                         |   47.37 |    48.27 |      60 |   47.37 | ...34-782,785-819 
  events.ts                            |   90.35 |    72.98 |     100 |   90.35 | ...22-625,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.47 |   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.42 |    81.41 |   88.54 |   86.42 |                   
  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                             |    85.5 |    80.68 |   79.68 |    85.5 | ...2205,2212-2213 
  logs.ts                              |    94.2 |    84.43 |     100 |    94.2 | ...17-624,680-683 
  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.71 |    79.31 |    90.9 |   63.71 | ...37-395,397-400 
 scripts                               |   88.49 |    65.82 |     100 |   88.49 |                   
  test-endpoints.ts                    |   97.35 |    77.77 |     100 |   97.35 | 143-145,187-188   
  useResource.ts                       |   83.57 |    62.29 |     100 |   83.57 | ...81-286,301-302 
---------------------------------------|---------|----------|---------|---------|-------------------

@aelmanaa

aelmanaa commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Review — PR #432 (DAPP-11562) feat(cct-sdk): Add CrossChainToken admin operations for EVM

  • Branch: feat/DAPP-11562-default-admin-transfercct-sdk · Reviewed head: 7f43e150 (1 commit) · Base: cct-sdk @ 11a6b1bc
  • Verdict: APPROVE — 4 new CrossChainToken v2.0.0 admin ops, correctly gated, grounded, consistent with siblings, and proven live. Plus one MAJOR (§4, an error-ordering bug in accept) — fixed + rebuilt + live-proven in the review worktree. Two minor NITs (§5). The MAJOR does not block merge (wrong message, not wrong behavior) but is worth folding in.
  • Change: adds beginDefaultAdminTransfer / acceptDefaultAdminTransfer / cancelDefaultAdminTransfer (OpenZeppelin AccessControlDefaultAdminRules two-step) and setCCIPAdmin, plus shared helpers in token/contracts.ts (resolveCrossChainToken, resolveTokenEncoder, readTokenDefaultAdmin, readPendingTokenDefaultAdmin, assertTokenDefaultAdmin) and 8 EVMTokenManager methods.
  • Method: three-lens review (types, architecture, DX) + on-chain grounding against chainlink-ccip + a live Ethereum-Sepolia run of every op and guard through the real token.

1. On-chain ground truth (chainlink-ccip, CrossChainToken v2.0.0) — quoted

CrossChainToken inherits OpenZeppelin AccessControlDefaultAdminRules (OZ 5.3.0) — CrossChainToken.sol:18 (is BaseERC20, AccessControlDefaultAdminRules, IBurnMintERC20); typeAndVersion = "CrossChainToken 2.0.0" (:19-21); constructor sets the ADAR delay to 0: AccessControlDefaultAdminRules(0, owner…) (:39).

The two-step admin functions are inherited unmodified from OZ ADAR (access/extensions/AccessControlDefaultAdminRules.sol):

  • beginDefaultAdminTransfer(address) … onlyRole(DEFAULT_ADMIN_ROLE) (:211)
  • cancelDefaultAdminTransfer() … onlyRole(DEFAULT_ADMIN_ROLE) (:229)
  • acceptDefaultAdminTransfer() requires _msgSender() == pendingDefaultAdmin (:245-250) and that the schedule has passed (_hasSchedulePassed is strict schedule < now, so even with delay 0 accept needs the next block)
  • defaultAdmin() (:170), pendingDefaultAdmin() → (address newAdmin, uint48 schedule) (:177)

Key claim — setCCIPAdmin gating. CrossChainToken overrides the base and re-gates to the default admin (CrossChainToken.sol:116-120):

function setCCIPAdmin(address newAdmin) external virtual override onlyRole(DEFAULT_ADMIN_ROLE) {
  _setCCIPAdmin(newAdmin);
}

This bypasses the v1.x-style base gate if (msg.sender != s_ccipAdmin) revert OnlyCCIPAdmin(); and the base zero-check if (newAdmin == address(0)) revert CannotRenounceCCIPAdmin(); (BaseERC20.sol:161-165); _setCCIPAdmin itself has no zero-check (BaseERC20.sol:172-179) → zero newAdmin is allowed. getCCIPAdmin() exists (BaseERC20.sol:150-152).

Renunciation model (OZ ADAR): beginDefaultAdminTransfer(address(0)) schedules a renunciation; acceptDefaultAdminTransfer can never complete it (_msgSender() can't be address(0)); it completes via renounceRole(DEFAULT_ADMIN_ROLE, …) after the delay (AccessControlDefaultAdminRules.sol:113-122).

So the SDK's gating maps 1:1 to on-chain authority: begin/cancel/setCCIPAdmin gate sender against defaultAdmin() (assertTokenDefaultAdmin); accept gates against pendingDefaultAdmin().newAdmin.


2. Structure & consistency (verified against siblings)

  • resolveCrossChainToken mirrors resolveTokenPool (reads typeAndVersion, throws the same CCTContractTypeInvalidError/CCTContractVersionUnsupportedError); returns just version since there is one token type.
  • resolveTokenEncoder is a structural copy of the token-pool resolveEncoder floor-match (same null-ceiling break), with a per-op encoders: Partial<Record<TokenVersion, Interface>> table — the established forward-compatible idiom.
  • Every op's execute override defaults sender→wallet via resolveWalletSender (matches transfer-token-ownership etc.).
  • Op names follow the on-chain function names; this DEFAULT_ADMIN_ROLE / ADAR concept is distinct from the token-admin-registry registerAdmin/transferAdmin family — no naming collision.
  • The assertTokenDefaultAdmin unconditional (begin/setCCIPAdmin) vs if (sender!==undefined)-wrapped (cancel) mix is principled: begin/setCCIPAdmin have no other pre-flight read so they keep the offline "has a default admin" existence check; cancel already gates existence via schedule === 0n.

@aelmanaa

Copy link
Copy Markdown
Collaborator

3. Live proof (Ethereum Sepolia, chainId 11155111) — verbatim

Fixtures (clickable Sepolia Etherscan links; every transaction is listed in full in §3.1a):

Thing Address
Token (CrossChainToken 2.0.0), defaultAdmin = me, delay 0 0x227f0db66e9bf42a28e1e38438c5f19d0adb2df0
Default admin / signer 0x9d087fC03ae39b088326b67fA3C788236645b717

3.1 Happy path, setCCIPAdmin round-trip, and guards (live, verbatim — full values)

--- begin(self) + accept(self) happy path (admin stays me) ---
 begin(self) tx: 0xbd082af02cdcd5745678000e9e92e4578a798d285343d78277b6829e27e63cd9  pending -> newAdmin=0x9d087fC03ae39b088326b67fA3C788236645b717 schedule=1790070636
 accept tx: 0x2418fa542c18392f5edb243cd81aeb215029c897ddd36cd1604f52542c33cb62  pending cleared -> schedule=0  defaultAdmin still 0x9d087fC03ae39b088326b67fA3C788236645b717

--- setCCIPAdmin round-trip (gated by DEFAULT_ADMIN_ROLE) ---
 getCCIPAdmin before: 0x9d087fC03ae39b088326b67fA3C788236645b717
 setCCIPAdmin(B) tx: 0x82980936bc0ad729cf7d775e18797b8f00f4d52b997b69b27796fbee77d5d7a3  -> getCCIPAdmin now: 0x00000000000000000000000000000000000000b2
 setCCIPAdmin(restore) tx: 0xa58ceeb6ff587ef7c2bc7ee391f359ed663abca05a7161ab181bd5f63f9e8b59  -> getCCIPAdmin now: 0x9d087fC03ae39b088326b67fA3C788236645b717

--- guards (build-time, no tx) ---
 begin, non-admin sender:        CCTParamsInvalidError param=sender  reason="must be the current default admin (0x9d087fC03ae39b088326b67fA3C788236645b717)"
 accept, no pending transfer:    CCTParamsInvalidError param=tokenAddress reason="has no pending default-admin transfer"
 setCCIPAdmin, non-admin sender: CCTParamsInvalidError param=sender  reason="must be the current default admin (0x9d087fC03ae39b088326b67fA3C788236645b717)"

(The accept timing revert AccessControlEnforcedDefaultAdminDelay (0x19ca5ebb) confirms the SDK correctly leaves the schedule check on-chain — accept needs the next block even at delay 0.)

3.1a Complete transaction list (every tx above, plus the begin+cancel cycle and cleanup — full hashes, each linked)

Op Tx (Sepolia Etherscan)
begin(→B) (first cycle) 0x94f659dadcbbc0db1dfc2b3f352574daf5266c2a71750a79a2a13f668f1f2fee
cancel (first cycle) 0xb54019ede4623a6fc73ef0862120b31ac0fa52d6e86d37867baa6557d383976f
begin(self) 0xbd082af02cdcd5745678000e9e92e4578a798d285343d78277b6829e27e63cd9
acceptDefaultAdminTransfer (self) 0x2418fa542c18392f5edb243cd81aeb215029c897ddd36cd1604f52542c33cb62
setCCIPAdmin(B) 0x82980936bc0ad729cf7d775e18797b8f00f4d52b997b69b27796fbee77d5d7a3
setCCIPAdmin(restore) 0xa58ceeb6ff587ef7c2bc7ee391f359ed663abca05a7161ab181bd5f63f9e8b59
begin(0) renunciation (bug capture) 0x50ca56d8a49133bb510d457576c96e96a69be42745829cc5386062c5d7d91935
cancel (restore after bug capture) 0xfb20878676ef1fe6895dff0031bccc871fb42d21ab8773259c467936c5c015c5
cleanup cancel (after the accept-timing revert) 0x0bfb38c5c838b51305812ee817c2aaace758c88f088e06f6a536ade4a1c315ba
begin(0) renunciation (fix proof) 0x12fc301fdbe8a74e8f6e53d999de90f69554dc3fe77e7fee99d10b1ec419b6be
cancel (restore after fix proof) 0x573bd36217e5e4ac106ce859bc00daa176b8714ca903e169fc18d061bbace99c
proposed admin B / new CCIP admin 0x00000000000000000000000000000000000000B2

3.2 Verification summary

Claim Evidence Live
begin→accept two-step works; admin retained on self-transfer §3.1 + §3.1a begin(self) & accept txs
setCCIPAdmin gated by default admin; changes CCIP admin on-chain; reversible §3.1 CCIP admin me→B→me (§3.1a setCCIPAdmin(B)/(restore))
begin/setCCIPAdmin reject a non-default-admin sender §3.1 guards
accept rejects when no transfer pending §3.1 guard
setCCIPAdmin re-gated to DEFAULT_ADMIN_ROLE (not v1.x CCIP admin); zero allowed §1 CrossChainToken.sol:116-120 ✅ (grounded)

@aelmanaa

Copy link
Copy Markdown
Collaborator

4. MAJOR — accept error ordering hides the renounceRole guidance (fixed + live-proven)

In accept-default-admin-transfer.ts buildUnsigned, the checks ran: (1) schedule === 0n, (2) sender !== newAdmin, (3) newAdmin === ZeroAddress. A pending renunciation has newAdmin = the zero address and schedule > 0, so with a known sender (and execute always sets sender = wallet) check (2) fires first and throws the nonsensical must be the pending default admin (0x0000000000000000000000000000000000000000) — telling the caller to become the zero address — instead of the actionable renunciation guidance in (3). So on the managed path the renounceRole message was unreachable (the PR's own test only exercised it with sender: undefined).

Proven live (unfixed code):

accept(renunciation pending, sender=me): CCTParamsInvalidError param=sender reason="must be the pending default admin (0x0000000000000000000000000000000000000000)"

Fix — move the renunciation check above the sender match:

if (schedule === 0n) throw new CCTParamsInvalidError(this.name, 'tokenAddress', 'has no pending default-admin transfer')
// checked before the sender match so this guidance wins over an impossible "must be the pending default admin (the zero address)"
if (newAdmin === ZeroAddress)
  throw new CCTParamsInvalidError(this.name, 'tokenAddress', 'has a pending default-admin renunciation, which must be completed with renounceRole')
if (sender !== undefined && getAddress(sender) !== newAdmin)
  throw new CCTParamsInvalidError(this.name, 'sender', `must be the pending default admin (${newAdmin})`)

Plus a regression test (gives the renounceRole guidance for a renunciation even when a sender is set). tsc/oxfmt/oxlint clean; accept-default-admin-transfer.test.ts 9/9.

Proven live (fixed code) — begin(0) tx 0x12fc301fdbe8a74e8f6e53d999de90f69554dc3fe77e7fee99d10b1ec419b6be:

accept(renunciation, sender=me): CCTParamsInvalidError param=tokenAddress reason="has a pending default-admin renunciation, which must be completed with renounceRole"
Bug proven live Fix built + green Fix proven live
§4 accept ordering ✅ wrong "must be the pending default admin (zero address)" message ✅ reorder + regression test; 9/9, tsc/lint 0 ✅ now the renounceRole guidance

5. NITs (optional, not applied)

  • as TokenVersion casts in resolveCrossChainToken — the sibling parseTokenPoolVersion avoids these via an is TokenPoolVersion predicate; a one-line isTokenVersion guard would match the pattern and drop both casts.
  • CCTOperationUnsupportedError (from resolveTokenEncoder) is omitted from the @throws — correct here, since resolveCrossChainToken pins v2.0.0 so the floor-match always resolves and the throw is unreachable for these ops.

6. Types / build / tests (verified in isolation)

Fresh worktree at 7f43e150 (clean, no leftover edits). SDK workspace:

committed head:  tsc --noEmit EXIT 0 · oxfmt EXIT 0 · oxlint EXIT 0 · 4 new op suites: tests 33, pass 33, fail 0
with §4 fix:     tsc EXIT 0 · oxfmt EXIT 0 · oxlint EXIT 0 · accept suite 9/9 (adds the sender-set renunciation test)

No any/unsafe as/! beyond the sibling-sanctioned versions[i]!. A full npm run check would EXIT 1 only on the routine generated-file drift (selectors.ts/api/index.ts), not this diff.


7. Recommendation

Approve. Four correctly-gated CrossChainToken admin ops, grounded to every relevant chainlink-ccip line (§1), sibling-consistent (§2), and proven live end-to-end (§3). Fold in the §4 MAJOR (accept error-ordering — fix implemented, green, live-proven before→after) before merge; the §5 NITs are optional. Nothing blocks the current diff's behavior.

@mervin-link

Copy link
Copy Markdown
Collaborator Author

4. MAJOR — accept error ordering hides the renounceRole guidance (fixed + live-proven)

In accept-default-admin-transfer.ts buildUnsigned, the checks ran: (1) schedule === 0n, (2) sender !== newAdmin, (3) newAdmin === ZeroAddress. A pending renunciation has newAdmin = the zero address and schedule > 0, so with a known sender (and execute always sets sender = wallet) check (2) fires first and throws the nonsensical must be the pending default admin (0x0000000000000000000000000000000000000000) — telling the caller to become the zero address — instead of the actionable renunciation guidance in (3). So on the managed path the renounceRole message was unreachable (the PR's own test only exercised it with sender: undefined).

Proven live (unfixed code):

accept(renunciation pending, sender=me): CCTParamsInvalidError param=sender reason="must be the pending default admin (0x0000000000000000000000000000000000000000)"

Fix — move the renunciation check above the sender match:

if (schedule === 0n) throw new CCTParamsInvalidError(this.name, 'tokenAddress', 'has no pending default-admin transfer')
// checked before the sender match so this guidance wins over an impossible "must be the pending default admin (the zero address)"
if (newAdmin === ZeroAddress)
  throw new CCTParamsInvalidError(this.name, 'tokenAddress', 'has a pending default-admin renunciation, which must be completed with renounceRole')
if (sender !== undefined && getAddress(sender) !== newAdmin)
  throw new CCTParamsInvalidError(this.name, 'sender', `must be the pending default admin (${newAdmin})`)

Plus a regression test (gives the renounceRole guidance for a renunciation even when a sender is set). tsc/oxfmt/oxlint clean; accept-default-admin-transfer.test.ts 9/9.

Proven live (fixed code) — begin(0) tx 0x12fc301fdbe8a74e8f6e53d999de90f69554dc3fe77e7fee99d10b1ec419b6be:

accept(renunciation, sender=me): CCTParamsInvalidError param=tokenAddress reason="has a pending default-admin renunciation, which must be completed with renounceRole"

Bug proven live Fix built + green Fix proven live
§4 accept ordering ✅ wrong "must be the pending default admin (zero address)" message ✅ reorder + regression test; 9/9, tsc/lint 0 ✅ now the renounceRole guidance

5. NITs (optional, not applied)

  • as TokenVersion casts in resolveCrossChainToken — the sibling parseTokenPoolVersion avoids these via an is TokenPoolVersion predicate; a one-line isTokenVersion guard would match the pattern and drop both casts.
  • CCTOperationUnsupportedError (from resolveTokenEncoder) is omitted from the @throws — correct here, since resolveCrossChainToken pins v2.0.0 so the floor-match always resolves and the throw is unreachable for these ops.

6. Types / build / tests (verified in isolation)

Fresh worktree at 7f43e150 (clean, no leftover edits). SDK workspace:

committed head:  tsc --noEmit EXIT 0 · oxfmt EXIT 0 · oxlint EXIT 0 · 4 new op suites: tests 33, pass 33, fail 0
with §4 fix:     tsc EXIT 0 · oxfmt EXIT 0 · oxlint EXIT 0 · accept suite 9/9 (adds the sender-set renunciation test)

No any/unsafe as/! beyond the sibling-sanctioned versions[i]!. A full npm run check would EXIT 1 only on the routine generated-file drift (selectors.ts/api/index.ts), not this diff.

7. Recommendation

Approve. Four correctly-gated CrossChainToken admin ops, grounded to every relevant chainlink-ccip line (§1), sibling-consistent (§2), and proven live end-to-end (§3). Fold in the §4 MAJOR (accept error-ordering — fix implemented, green, live-proven before→after) before merge; the §5 NITs are optional. Nothing blocks the current diff's behavior.

@aelmanaa Addressed the validation ordering for accept and also the two nits.

This branch has not been deployed

No deployments
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