@chainlink/ccip-sdk 0.93.0 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/aptos/index.d.ts.map +1 -1
  2. package/dist/aptos/index.js +9 -5
  3. package/dist/aptos/index.js.map +1 -1
  4. package/dist/aptos/send.js +1 -1
  5. package/dist/aptos/send.js.map +1 -1
  6. package/dist/chain.d.ts +13 -6
  7. package/dist/chain.d.ts.map +1 -1
  8. package/dist/chain.js +14 -0
  9. package/dist/chain.js.map +1 -1
  10. package/dist/evm/index.d.ts.map +1 -1
  11. package/dist/evm/index.js +14 -10
  12. package/dist/evm/index.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/requests.d.ts +8 -2
  16. package/dist/requests.d.ts.map +1 -1
  17. package/dist/requests.js +10 -0
  18. package/dist/requests.js.map +1 -1
  19. package/dist/selectors.d.ts.map +1 -1
  20. package/dist/selectors.js +20 -0
  21. package/dist/selectors.js.map +1 -1
  22. package/dist/solana/index.d.ts +7 -3
  23. package/dist/solana/index.d.ts.map +1 -1
  24. package/dist/solana/index.js +64 -7
  25. package/dist/solana/index.js.map +1 -1
  26. package/dist/sui/index.d.ts +14 -9
  27. package/dist/sui/index.d.ts.map +1 -1
  28. package/dist/sui/index.js +47 -19
  29. package/dist/sui/index.js.map +1 -1
  30. package/dist/sui/manuallyExec/encoder.d.ts.map +1 -1
  31. package/dist/sui/manuallyExec/encoder.js +1 -0
  32. package/dist/sui/manuallyExec/encoder.js.map +1 -1
  33. package/dist/sui/manuallyExec/index.d.ts.map +1 -1
  34. package/dist/sui/manuallyExec/index.js +1 -0
  35. package/dist/sui/manuallyExec/index.js.map +1 -1
  36. package/dist/sui/objects.d.ts.map +1 -1
  37. package/dist/sui/objects.js +2 -1
  38. package/dist/sui/objects.js.map +1 -1
  39. package/dist/ton/hasher.d.ts.map +1 -1
  40. package/dist/ton/hasher.js +1 -0
  41. package/dist/ton/hasher.js.map +1 -1
  42. package/dist/ton/index.d.ts.map +1 -1
  43. package/dist/ton/index.js +1 -0
  44. package/dist/ton/index.js.map +1 -1
  45. package/dist/types.d.ts +10 -2
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/utils.d.ts +1 -0
  48. package/dist/utils.d.ts.map +1 -1
  49. package/dist/utils.js +1 -0
  50. package/dist/utils.js.map +1 -1
  51. package/package.json +16 -10
  52. package/src/aptos/index.ts +20 -6
  53. package/src/aptos/send.ts +1 -1
  54. package/src/chain.ts +27 -3
  55. package/src/evm/index.ts +25 -9
  56. package/src/index.ts +1 -1
  57. package/src/requests.ts +13 -1
  58. package/src/selectors.ts +20 -0
  59. package/src/solana/index.ts +99 -7
  60. package/src/sui/index.ts +61 -28
  61. package/src/sui/manuallyExec/encoder.ts +2 -0
  62. package/src/sui/manuallyExec/index.ts +2 -0
  63. package/src/sui/objects.ts +3 -1
  64. package/src/ton/hasher.ts +2 -0
  65. package/src/ton/index.ts +2 -0
  66. package/src/types.ts +11 -2
  67. package/src/utils.ts +2 -0
package/src/chain.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { BytesLike } from 'ethers'
1
+ import { type BytesLike, dataLength } from 'ethers'
2
2
  import type { PickDeep, SetOptional } from 'type-fest'
3
3
 
4
4
  import { type LaneLatencyResponse, CCIPAPIClient } from './api/index.ts'
@@ -10,6 +10,7 @@ import {
10
10
  CCIPExecTxRevertedError,
11
11
  CCIPTransactionNotFinalizedError,
12
12
  } from './errors/index.ts'
13
+ import { DEFAULT_GAS_LIMIT } from './evm/const.ts'
13
14
  import type { UnsignedEVMTx } from './evm/types.ts'
14
15
  import type {
15
16
  EVMExtraArgsV1,
@@ -37,6 +38,7 @@ import {
37
38
  type Logger,
38
39
  type NetworkInfo,
39
40
  type OffchainTokenData,
41
+ type MessageInput,
40
42
  type WithLogger,
41
43
  ExecutionState,
42
44
  } from './types.ts'
@@ -153,7 +155,7 @@ export type UnsignedTx = {
153
155
  }
154
156
 
155
157
  /**
156
- * Common options for [[generateUnsignedSendMessage]] and [[sendMessage]] Chain methods
158
+ * Common options for [[getFee]], [[generateUnsignedSendMessage]] and [[sendMessage]] Chain methods
157
159
  */
158
160
  export type SendMessageOpts = {
159
161
  /** Router address on this chain */
@@ -161,7 +163,7 @@ export type SendMessageOpts = {
161
163
  /** Destination network selector. */
162
164
  destChainSelector: bigint
163
165
  /** Message to send. If `fee` is omitted, it'll be calculated */
164
- message: AnyMessage & { fee?: bigint }
166
+ message: MessageInput
165
167
  /** Approve the maximum amount of tokens to transfer */
166
168
  approveMax?: boolean
167
169
  }
@@ -690,6 +692,21 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
690
692
  * @returns Mapping of token addresses to respective TokenInfo objects.
691
693
  */
692
694
  abstract getFeeTokens(router: string): Promise<Record<string, TokenInfo>>
695
+
696
+ /** {@inheritDoc ChainStatic.buildMessageForDest} */
697
+ static buildMessageForDest(
698
+ message: Parameters<ChainStatic['buildMessageForDest']>[0],
699
+ ): AnyMessage {
700
+ // default to GenericExtraArgsV2, aka EVMExtraArgsV2
701
+ return {
702
+ ...message,
703
+ extraArgs: {
704
+ gasLimit: message.data && dataLength(message.data) ? DEFAULT_GAS_LIMIT : 0n,
705
+ allowOutOfOrderExecution: true,
706
+ ...message.extraArgs,
707
+ },
708
+ }
709
+ }
693
710
  }
694
711
 
695
712
  /** Static methods and properties available on Chain class constructors. */
@@ -775,6 +792,13 @@ export type ChainStatic<F extends ChainFamily = ChainFamily> = Function & {
775
792
  * @returns Ordered record with messages/properties, or undefined if not a recognized error
776
793
  */
777
794
  parse?(data: unknown): Record<string, unknown> | undefined | null
795
+ /**
796
+ * Returns a copy of a message, populating missing fields like `extraArgs` with defaults
797
+ * It's expected to return a message suitable at least for basic token transfers
798
+ * @param message - AnyMessage (from source), containing at least `receiver`
799
+ * @returns A message suitable for `sendMessage` to this destination chain family
800
+ */
801
+ buildMessageForDest(message: MessageInput): AnyMessage
778
802
  }
779
803
 
780
804
  /** Function type for getting a Chain instance by ID, selector, or name. */
package/src/evm/index.ts CHANGED
@@ -116,7 +116,12 @@ import {
116
116
  parseSourceTokenData,
117
117
  } from './messages.ts'
118
118
  import { encodeEVMOffchainTokenData, fetchEVMOffchainTokenData } from './offchain.ts'
119
- import { getMessageById, getMessagesInBatch, getMessagesInTx } from '../requests.ts'
119
+ import {
120
+ buildMessageForDest,
121
+ getMessageById,
122
+ getMessagesInBatch,
123
+ getMessagesInTx,
124
+ } from '../requests.ts'
120
125
  import type { UnsignedEVMTx } from './types.ts'
121
126
  export type { UnsignedEVMTx }
122
127
 
@@ -960,17 +965,20 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
960
965
  destChainSelector,
961
966
  message,
962
967
  }: Parameters<Chain['getFee']>[0]): Promise<bigint> {
968
+ const populatedMessage = buildMessageForDest(message, networkInfo(destChainSelector).family)
963
969
  const contract = new Contract(
964
970
  router,
965
971
  interfaces.Router,
966
972
  this.provider,
967
973
  ) as unknown as TypedContract<typeof Router_ABI>
968
974
  return contract.getFee(destChainSelector, {
969
- receiver: zeroPadValue(getAddressBytes(message.receiver), 32),
970
- data: hexlify(message.data),
971
- tokenAmounts: message.tokenAmounts ?? [],
972
- feeToken: message.feeToken ?? ZeroAddress,
973
- extraArgs: hexlify((this.constructor as typeof EVMChain).encodeExtraArgs(message.extraArgs)),
975
+ receiver: zeroPadValue(getAddressBytes(populatedMessage.receiver), 32),
976
+ data: hexlify(populatedMessage.data ?? '0x'),
977
+ tokenAmounts: populatedMessage.tokenAmounts ?? [],
978
+ feeToken: populatedMessage.feeToken ?? ZeroAddress,
979
+ extraArgs: hexlify(
980
+ (this.constructor as typeof EVMChain).encodeExtraArgs(populatedMessage.extraArgs),
981
+ ),
974
982
  })
975
983
  }
976
984
 
@@ -982,11 +990,19 @@ export class EVMChain extends Chain<typeof ChainFamily.EVM> {
982
990
  async generateUnsignedSendMessage(
983
991
  opts: Parameters<Chain['generateUnsignedSendMessage']>[0],
984
992
  ): Promise<UnsignedEVMTx> {
985
- const { sender, router, destChainSelector, message } = opts
986
- if (!message.fee) message.fee = await this.getFee(opts)
993
+ const { sender, router, destChainSelector } = opts
994
+ const populatedMessage = buildMessageForDest(
995
+ opts.message,
996
+ networkInfo(destChainSelector).family,
997
+ )
998
+ const message = {
999
+ ...populatedMessage,
1000
+ fee: opts.message.fee ?? (await this.getFee({ ...opts, message: populatedMessage })),
1001
+ }
1002
+
987
1003
  const feeToken = message.feeToken ?? ZeroAddress
988
1004
  const receiver = zeroPadValue(getAddressBytes(message.receiver), 32)
989
- const data = hexlify(message.data)
1005
+ const data = hexlify(message.data ?? '0x')
990
1006
  const extraArgs = hexlify(
991
1007
  (this.constructor as typeof EVMChain).encodeExtraArgs(message.extraArgs),
992
1008
  )
package/src/index.ts CHANGED
@@ -24,7 +24,6 @@ export {
24
24
  export { estimateExecGasForRequest } from './gas.ts'
25
25
  export { decodeMessage, getMessagesForSender, sourceToDestTokenAmounts } from './requests.ts'
26
26
  export {
27
- type AnyMessage,
28
27
  type CCIPCommit,
29
28
  type CCIPExecution,
30
29
  type CCIPMessage,
@@ -37,6 +36,7 @@ export {
37
36
  type Logger,
38
37
  type NetworkInfo,
39
38
  type OffchainTokenData,
39
+ type MessageInput,
40
40
  type WithLogger,
41
41
  CCIPVersion,
42
42
  ExecutionState,
package/src/requests.ts CHANGED
@@ -2,7 +2,7 @@ import { isBytesLike, toBigInt } from 'ethers'
2
2
  import type { PickDeep } from 'type-fest'
3
3
  import yaml from 'yaml'
4
4
 
5
- import type { Chain, ChainStatic, LogFilter } from './chain.ts'
5
+ import { type ChainStatic, type LogFilter, Chain } from './chain.ts'
6
6
  import {
7
7
  CCIPMessageBatchIncompleteError,
8
8
  CCIPMessageDecodeError,
@@ -16,11 +16,13 @@ import type { EVMChain } from './evm/index.ts'
16
16
  import { decodeExtraArgs } from './extra-args.ts'
17
17
  import { supportedChains } from './supported-chains.ts'
18
18
  import {
19
+ type AnyMessage,
19
20
  type CCIPMessage,
20
21
  type CCIPRequest,
21
22
  type CCIPVersion,
22
23
  type ChainTransaction,
23
24
  type Log_,
25
+ type MessageInput,
24
26
  ChainFamily,
25
27
  } from './types.ts'
26
28
  import { convertKeysToCamelCase, decodeAddress, leToBigInt, networkInfo } from './utils.ts'
@@ -109,6 +111,16 @@ export function decodeMessage(data: string | Uint8Array | Record<string, unknown
109
111
  throw new CCIPMessageDecodeError()
110
112
  }
111
113
 
114
+ /**
115
+ * Populates missing required fields (e.g. `extraArgs`) from AnyMessage
116
+ * @param message - partial AnyMessage
117
+ * @returns original message or shallow copy with defaults for required fields
118
+ **/
119
+ export function buildMessageForDest(message: MessageInput, dest: ChainFamily): AnyMessage {
120
+ const chain = supportedChains[dest] ?? Chain
121
+ return chain.buildMessageForDest(message)
122
+ }
123
+
112
124
  /**
113
125
  * Fetch all CCIP messages in a transaction
114
126
  * @param source - Chain
package/src/selectors.ts CHANGED
@@ -453,6 +453,11 @@ const selectors: Selectors = {
453
453
  name: 'story-testnet',
454
454
  family: 'evm',
455
455
  },
456
+ '1672': {
457
+ selector: 7801139999541420232n,
458
+ name: 'pharos-mainnet',
459
+ family: 'evm',
460
+ },
456
461
  '1687': {
457
462
  selector: 10749384167430721561n,
458
463
  name: 'mint-testnet',
@@ -759,6 +764,11 @@ const selectors: Selectors = {
759
764
  name: 'ethereum-testnet-sepolia-immutable-zkevm-1',
760
765
  family: 'evm',
761
766
  },
767
+ '14601': {
768
+ selector: 1763698235108410440n,
769
+ name: 'sonic-testnet',
770
+ family: 'evm',
771
+ },
762
772
  '16600': {
763
773
  selector: 16088006396410204581n,
764
774
  name: '0g-testnet-newton',
@@ -807,6 +817,11 @@ const selectors: Selectors = {
807
817
  family: 'evm',
808
818
  },
809
819
  '36888': { selector: 4829375610284793157n, name: 'ab-mainnet', family: 'evm' },
820
+ '36900': {
821
+ selector: 4059281736450291836n,
822
+ name: 'adi-mainnet',
823
+ family: 'evm',
824
+ },
810
825
  '37111': {
811
826
  selector: 6827576821754315911n,
812
827
  name: 'ethereum-testnet-sepolia-lens-1',
@@ -1009,6 +1024,11 @@ const selectors: Selectors = {
1009
1024
  name: 'plume-testnet-sepolia',
1010
1025
  family: 'evm',
1011
1026
  },
1027
+ '99999': {
1028
+ selector: 9418205736192840573n,
1029
+ name: 'adi-testnet',
1030
+ family: 'evm',
1031
+ },
1012
1032
  '128123': {
1013
1033
  selector: 1910019406958449359n,
1014
1034
  name: 'etherlink-testnet',
@@ -26,16 +26,18 @@ import {
26
26
  toBigInt,
27
27
  } from 'ethers'
28
28
  import { type Memoized, memoize } from 'micro-memoize'
29
- import type { PickDeep, SetRequired } from 'type-fest'
29
+ import type { PickDeep } from 'type-fest'
30
30
 
31
31
  import {
32
32
  type ChainContext,
33
+ type ChainStatic,
33
34
  type LogFilter,
34
35
  type TokenInfo,
35
36
  type TokenPoolRemote,
36
37
  Chain,
37
38
  } from '../chain.ts'
38
39
  import {
40
+ CCIPArgumentInvalidError,
39
41
  CCIPBlockTimeNotFoundError,
40
42
  CCIPContractNotRouterError,
41
43
  CCIPDataFormatUnsupportedError,
@@ -59,11 +61,17 @@ import {
59
61
  CCIPTransactionNotFoundError,
60
62
  CCIPWalletInvalidError,
61
63
  } from '../errors/index.ts'
62
- import { type EVMExtraArgsV2, type ExtraArgs, EVMExtraArgsV2Tag } from '../extra-args.ts'
64
+ import {
65
+ type EVMExtraArgsV2,
66
+ type ExtraArgs,
67
+ type SVMExtraArgsV1,
68
+ EVMExtraArgsV2Tag,
69
+ } from '../extra-args.ts'
63
70
  import type { LeafHasher } from '../hasher/common.ts'
64
71
  import SELECTORS from '../selectors.ts'
65
72
  import { supportedChains } from '../supported-chains.ts'
66
73
  import {
74
+ type AnyMessage,
67
75
  type CCIPCommit,
68
76
  type CCIPExecution,
69
77
  type CCIPMessage,
@@ -114,8 +122,14 @@ import {
114
122
  simulateAndSendTxs,
115
123
  simulationProvider,
116
124
  } from './utils.ts'
117
- import { getMessageById, getMessagesInBatch, getMessagesInTx } from '../requests.ts'
125
+ import {
126
+ buildMessageForDest,
127
+ getMessageById,
128
+ getMessagesInBatch,
129
+ getMessagesInTx,
130
+ } from '../requests.ts'
118
131
  import { patchBorsh } from './patchBorsh.ts'
132
+ import { DEFAULT_GAS_LIMIT } from '../evm/const.ts'
119
133
  export type { UnsignedSolanaTx }
120
134
 
121
135
  const routerCoder = new BorshCoder(CCIP_ROUTER_IDL)
@@ -976,7 +990,8 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
976
990
 
977
991
  /** {@inheritDoc Chain.getFee} */
978
992
  getFee({ router, destChainSelector, message }: Parameters<Chain['getFee']>[0]): Promise<bigint> {
979
- return getFee(this, router, destChainSelector, message)
993
+ const populatedMessage = buildMessageForDest(message, networkInfo(destChainSelector).family)
994
+ return getFee(this, router, destChainSelector, populatedMessage)
980
995
  }
981
996
 
982
997
  /**
@@ -988,14 +1003,21 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
988
1003
  async generateUnsignedSendMessage(
989
1004
  opts: Parameters<Chain['generateUnsignedSendMessage']>[0],
990
1005
  ): Promise<UnsignedSolanaTx> {
991
- const { sender, router, destChainSelector, message } = opts
992
- if (!message.fee) message.fee = await this.getFee(opts)
1006
+ const { sender, router, destChainSelector } = opts
1007
+ const populatedMessage = buildMessageForDest(
1008
+ opts.message,
1009
+ networkInfo(destChainSelector).family,
1010
+ )
1011
+ const message = {
1012
+ ...populatedMessage,
1013
+ fee: opts.message.fee ?? (await this.getFee({ ...opts, message: populatedMessage })),
1014
+ }
993
1015
  return generateUnsignedCcipSend(
994
1016
  this,
995
1017
  new PublicKey(sender),
996
1018
  new PublicKey(router),
997
1019
  destChainSelector,
998
- message as SetRequired<typeof message, 'fee'>,
1020
+ message,
999
1021
  opts,
1000
1022
  )
1001
1023
  }
@@ -1479,4 +1501,74 @@ export class SolanaChain extends Chain<typeof ChainFamily.Solana> {
1479
1501
  // the offramps, so we can use it to narrow the search for the offramp
1480
1502
  return program.account.config.fetch(configPda)
1481
1503
  }
1504
+
1505
+ /** {@inheritDoc ChainStatic.buildMessageForDest} */
1506
+ static override buildMessageForDest(
1507
+ message: Parameters<ChainStatic['buildMessageForDest']>[0],
1508
+ ): AnyMessage & { extraArgs: SVMExtraArgsV1 } {
1509
+ if (
1510
+ !(
1511
+ message.extraArgs &&
1512
+ 'tokenReceiver' in message.extraArgs &&
1513
+ message.extraArgs.tokenReceiver
1514
+ ) &&
1515
+ message.data &&
1516
+ getDataBytes(message.data).length &&
1517
+ message.tokenAmounts?.length
1518
+ )
1519
+ throw new CCIPArgumentInvalidError(
1520
+ 'tokenReceiver',
1521
+ 'required when sending tokens with data to Solana',
1522
+ )
1523
+
1524
+ const computeUnits =
1525
+ message.extraArgs &&
1526
+ 'computeUnits' in message.extraArgs &&
1527
+ message.extraArgs.computeUnits != null
1528
+ ? message.extraArgs.computeUnits
1529
+ : message.extraArgs && 'gasLimit' in message.extraArgs && message.extraArgs.gasLimit != null
1530
+ ? message.extraArgs.gasLimit // populates computeUnits from gasLimit
1531
+ : message.data && getDataBytes(message.data).length
1532
+ ? DEFAULT_GAS_LIMIT
1533
+ : 0n
1534
+ const allowOutOfOrderExecution =
1535
+ message.extraArgs &&
1536
+ 'allowOutOfOrderExecution' in message.extraArgs &&
1537
+ message.extraArgs.allowOutOfOrderExecution != null
1538
+ ? message.extraArgs.allowOutOfOrderExecution
1539
+ : true
1540
+ const tokenReceiver =
1541
+ message.extraArgs &&
1542
+ 'tokenReceiver' in message.extraArgs &&
1543
+ message.extraArgs.tokenReceiver != null
1544
+ ? message.extraArgs.tokenReceiver
1545
+ : message.tokenAmounts?.length
1546
+ ? this.getAddress(message.receiver)
1547
+ : PublicKey.default.toBase58()
1548
+ const accounts =
1549
+ message.extraArgs && 'accounts' in message.extraArgs && message.extraArgs.accounts != null
1550
+ ? message.extraArgs.accounts
1551
+ : []
1552
+ const accountIsWritableBitmap =
1553
+ message.extraArgs &&
1554
+ 'accountIsWritableBitmap' in message.extraArgs &&
1555
+ message.extraArgs.accountIsWritableBitmap != null
1556
+ ? message.extraArgs.accountIsWritableBitmap
1557
+ : 0n
1558
+
1559
+ const extraArgs: SVMExtraArgsV1 = {
1560
+ computeUnits,
1561
+ allowOutOfOrderExecution,
1562
+ tokenReceiver,
1563
+ accounts,
1564
+ accountIsWritableBitmap,
1565
+ }
1566
+
1567
+ return {
1568
+ ...message,
1569
+ extraArgs,
1570
+ // if tokenReceiver, then message.receiver can (must?) be default
1571
+ ...(!!message.tokenAmounts?.length && { receiver: PublicKey.default.toBase58() }),
1572
+ }
1573
+ }
1482
1574
  }
package/src/sui/index.ts CHANGED
@@ -1,29 +1,30 @@
1
- import { toHex } from '@mysten/bcs'
1
+ import { Buffer } from 'buffer'
2
+
2
3
  import { type SuiTransactionBlockResponse, SuiClient } from '@mysten/sui/client'
3
4
  import type { Keypair } from '@mysten/sui/cryptography'
4
5
  import { SuiGraphQLClient } from '@mysten/sui/graphql'
5
6
  import { Transaction } from '@mysten/sui/transactions'
6
- import { type BytesLike, AbiCoder, hexlify, isBytesLike } from 'ethers'
7
+ import { type BytesLike, dataLength, hexlify, isBytesLike } from 'ethers'
7
8
  import type { PickDeep } from 'type-fest'
8
9
 
9
10
  import { AptosChain } from '../aptos/index.ts'
10
- import { type ChainContext, type LogFilter, Chain } from '../chain.ts'
11
+ import { type ChainContext, type ChainStatic, type LogFilter, Chain } from '../chain.ts'
11
12
  import {
12
13
  CCIPContractNotRouterError,
13
14
  CCIPDataFormatUnsupportedError,
14
15
  CCIPError,
15
16
  CCIPErrorCode,
16
17
  CCIPExecTxRevertedError,
17
- CCIPExtraArgsInvalidError,
18
18
  CCIPNotImplementedError,
19
19
  CCIPSuiMessageVersionInvalidError,
20
20
  CCIPVersionFeatureUnavailableError,
21
21
  } from '../errors/index.ts'
22
- import type { ExtraArgs, SuiExtraArgsV1 } from '../extra-args.ts'
22
+ import type { EVMExtraArgsV2, ExtraArgs, SVMExtraArgsV1, SuiExtraArgsV1 } from '../extra-args.ts'
23
23
  import { getSuiLeafHasher } from './hasher.ts'
24
24
  import type { LeafHasher } from '../hasher/common.ts'
25
25
  import { supportedChains } from '../supported-chains.ts'
26
26
  import {
27
+ type AnyMessage,
27
28
  type CCIPExecution,
28
29
  type CCIPMessage,
29
30
  type CCIPRequest,
@@ -42,7 +43,7 @@ import {
42
43
  } from '../types.ts'
43
44
  import { discoverCCIP, discoverOfframp } from './discovery.ts'
44
45
  import type { CCIPMessage_V1_6_Sui } from './types.ts'
45
- import { bytesToBuffer, decodeAddress, getDataBytes, networkInfo } from '../utils.ts'
46
+ import { bytesToBuffer, decodeAddress, networkInfo } from '../utils.ts'
46
47
  import { type CommitEvent, getSuiEventsInTimeRange } from './events.ts'
47
48
  import {
48
49
  type SuiManuallyExecuteInput,
@@ -57,6 +58,7 @@ import {
57
58
  } from './objects.ts'
58
59
 
59
60
  export const SUI_EXTRA_ARGS_V1_TAG = '21ea4ca9' as const
61
+ const DEFAULT_GAS_LIMIT = 1000000n
60
62
 
61
63
  type SuiContractDir = {
62
64
  ccip?: string
@@ -438,28 +440,11 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
438
440
  */
439
441
  static decodeExtraArgs(
440
442
  extraArgs: BytesLike,
441
- ): (SuiExtraArgsV1 & { _tag: 'SuiExtraArgsV1' }) | undefined {
442
- const data = getDataBytes(extraArgs)
443
- const hexBytes = toHex(data)
444
- if (!hexBytes.startsWith(SUI_EXTRA_ARGS_V1_TAG)) {
445
- throw new CCIPExtraArgsInvalidError('Sui', hexBytes)
446
- }
447
-
448
- const abiData = '0x' + hexBytes.slice(8)
449
- const decoded = AbiCoder.defaultAbiCoder().decode(
450
- ['tuple(uint256,bool,bytes32,bytes32[])'],
451
- abiData,
452
- )
453
-
454
- const tuple = decoded[0] as readonly [bigint, boolean, string, string[]]
455
-
456
- return {
457
- gasLimit: tuple[0],
458
- allowOutOfOrderExecution: tuple[1],
459
- tokenReceiver: tuple[2],
460
- receiverObjectIds: tuple[3], // Already an array of hex strings
461
- _tag: 'SuiExtraArgsV1',
462
- }
443
+ ):
444
+ | (EVMExtraArgsV2 & { _tag: 'EVMExtraArgsV2' })
445
+ | (SVMExtraArgsV1 & { _tag: 'SVMExtraArgsV1' })
446
+ | undefined {
447
+ return AptosChain.decodeExtraArgs(extraArgs)
463
448
  }
464
449
 
465
450
  /**
@@ -743,4 +728,52 @@ export class SuiChain extends Chain<typeof ChainFamily.Sui> {
743
728
  async getFeeTokens(_router: string): Promise<never> {
744
729
  return Promise.reject(new CCIPNotImplementedError('SuiChain.getFeeTokens'))
745
730
  }
731
+
732
+ /** {@inheritDoc ChainStatic.buildMessageForDest} */
733
+ static override buildMessageForDest(
734
+ message: Parameters<ChainStatic['buildMessageForDest']>[0],
735
+ ): AnyMessage & { extraArgs: SuiExtraArgsV1 } {
736
+ const gasLimit =
737
+ message.extraArgs && 'gasLimit' in message.extraArgs && message.extraArgs.gasLimit != null
738
+ ? message.extraArgs.gasLimit
739
+ : message.data && dataLength(message.data)
740
+ ? DEFAULT_GAS_LIMIT
741
+ : 0n
742
+ const allowOutOfOrderExecution =
743
+ message.extraArgs &&
744
+ 'allowOutOfOrderExecution' in message.extraArgs &&
745
+ message.extraArgs.allowOutOfOrderExecution != null
746
+ ? message.extraArgs.allowOutOfOrderExecution
747
+ : true
748
+ const tokenReceiver =
749
+ message.extraArgs &&
750
+ 'tokenReceiver' in message.extraArgs &&
751
+ message.extraArgs.tokenReceiver != null
752
+ ? message.extraArgs.tokenReceiver
753
+ : message.tokenAmounts?.length
754
+ ? this.getAddress(message.receiver)
755
+ : '0x0000000000000000000000000000000000000000000000000000000000000000'
756
+ const receiverObjectIds =
757
+ message.extraArgs &&
758
+ 'receiverObjectIds' in message.extraArgs &&
759
+ message.extraArgs.receiverObjectIds?.length
760
+ ? message.extraArgs.receiverObjectIds
761
+ : message.extraArgs && 'accounts' in message.extraArgs && message.extraArgs.accounts?.length
762
+ ? message.extraArgs.accounts // populates receiverObjectIds from accounts
763
+ : []
764
+ const extraArgs: SuiExtraArgsV1 = {
765
+ gasLimit,
766
+ allowOutOfOrderExecution,
767
+ tokenReceiver,
768
+ receiverObjectIds,
769
+ }
770
+ return {
771
+ ...message,
772
+ extraArgs,
773
+ // if tokenReceiver, then message.receiver can (must?) be default
774
+ ...(!!message.tokenAmounts?.length && {
775
+ receiver: '0x0000000000000000000000000000000000000000000000000000000000000000',
776
+ }),
777
+ }
778
+ }
746
779
  }
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import { bcs } from '@mysten/sui/bcs'
2
4
  import type { BytesLike } from 'ethers'
3
5
 
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import { Transaction } from '@mysten/sui/transactions'
2
4
 
3
5
  import { serializeExecutionReport } from './encoder.ts'
@@ -1,8 +1,10 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import { bcs } from '@mysten/sui/bcs'
2
4
  import type { SuiClient } from '@mysten/sui/client'
3
5
  import { Transaction } from '@mysten/sui/transactions'
4
6
  import { normalizeSuiAddress } from '@mysten/sui/utils'
5
- import { blake2b } from '@noble/hashes/blake2'
7
+ import { blake2b } from '@noble/hashes/blake2.js'
6
8
 
7
9
  import { CCIPDataFormatUnsupportedError } from '../errors/index.ts'
8
10
  import type { CCIPMessage, CCIPVersion } from '../types.ts'
package/src/ton/hasher.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import { type Cell, Address, beginCell } from '@ton/core'
2
4
  import { sha256, toBigInt } from 'ethers'
3
5
 
package/src/ton/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import { type Transaction, Address, Cell, beginCell, toNano } from '@ton/core'
2
4
  import { TonClient } from '@ton/ton'
3
5
  import { type AxiosAdapter, getAdapter } from 'axios'
package/src/types.ts CHANGED
@@ -294,12 +294,21 @@ export type ExecutionReport<M extends CCIPMessage = CCIPMessage> = {
294
294
  export type AnyMessage = {
295
295
  /** Receiver address on the destination chain. */
296
296
  receiver: BytesLike
297
- /** Arbitrary data payload. */
298
- data: BytesLike
299
297
  /** Extra arguments for gas limits and other settings. */
300
298
  extraArgs: ExtraArgs
299
+ /** Arbitrary data payload. */
300
+ data?: BytesLike
301
301
  /** Optional token transfers. */
302
302
  tokenAmounts?: readonly { token: string; amount: bigint }[]
303
303
  /** Optional fee token address (native if omitted). */
304
304
  feeToken?: string
305
305
  }
306
+
307
+ /**
308
+ * Partial [[AnyMessage]], which populates default fields like `extraArgs` if needed
309
+ */
310
+ export type MessageInput = Partial<AnyMessage> & {
311
+ receiver: AnyMessage['receiver']
312
+ extraArgs?: Partial<ExtraArgs>
313
+ fee?: bigint
314
+ }
package/src/utils.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'buffer'
2
+
1
3
  import bs58 from 'bs58'
2
4
  import {
3
5
  type BigNumberish,