Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ const fee = await source.getFee({ router, destChainSelector, message: {
| Chain Family | Networks | Status |
| ------------ | ------------------------------------------------------------ | -------------- |
| EVM | Ethereum, Arbitrum, Optimism, Polygon, Avalanche, Base, etc. | Supported |
| EVM (Hedera) | Mainnet, Testnet | Supported (native HBAR fee) |
| Solana | Mainnet, Devnet | Supported |
| Aptos | Mainnet, Testnet | Supported |
| Sui | Mainnet, Testnet | Partial (manual exec) |
| Sui | Mainnet, Testnet | Partial (no token pool queries) |
| TON | Mainnet, Testnet | Partial (no token pool/registry queries) |

## Documentation
Expand Down
26 changes: 21 additions & 5 deletions ccip-cli/src/commands/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
getDataBytes,
networkInfo,
} from '@chainlink/ccip-sdk/src/index.ts'
import { type BytesLike, AbiCoder, formatUnits, toUtf8Bytes } from 'ethers'
import { type BytesLike, AbiCoder, ZeroAddress, formatUnits, toUtf8Bytes } from 'ethers'
import type { Argv } from 'yargs'

import type { GlobalOpts } from '../index.ts'
Expand Down Expand Up @@ -318,7 +318,14 @@ async function sendMessage(
}

let feeToken, feeTokenInfo
if (argv.feeToken) {
const feeTokenArg = (argv.feeToken ?? '').toLowerCase()
if (feeTokenArg === 'native' || feeTokenArg === 'hbar') {
// CCIP Directory lists native HBAR as a fee token on Hedera; use ZeroAddress so EVM sends msg.value
feeToken = ZeroAddress
const wrappedNative = await source.getNativeTokenForRouter(argv.router)
feeTokenInfo = await source.getTokenInfo(wrappedNative)
feeTokenInfo = { ...feeTokenInfo, symbol: feeTokenInfo.symbol.replace(/^W/, '') || 'HBAR' }
} else if (argv.feeToken) {
Comment on lines +321 to +328
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This doesn't seem like the right abstraction level to do. this. Can this logic be moved down to the implementations of getTokenInfo? I don't know much about Hedera but it seems like we're bleeding pretty chain and token-specific behaviour into a file that's chain-agnostic.

try {
feeToken = (source.constructor as ChainStatic).getAddress(argv.feeToken)
feeTokenInfo = await source.getTokenInfo(feeToken)
Expand All @@ -336,6 +343,13 @@ async function sendMessage(
} else {
const nativeToken = await source.getNativeTokenForRouter(argv.router)
feeTokenInfo = await source.getTokenInfo(nativeToken)
if (feeTokenInfo.symbol === 'WHBAR') {
// On Hedera, default to native HBAR (msg.value) rather than WHBAR approval
feeToken = ZeroAddress
feeTokenInfo = { ...feeTokenInfo, symbol: 'HBAR' }
} else {
feeToken = nativeToken
}
}

const message: MessageInput = {
Expand Down Expand Up @@ -371,9 +385,11 @@ async function sendMessage(
const balance = await source.getBalance({ holder: walletAddress, token: feeToken })
if (balance < fee) {
const symbol =
feeTokenInfo.symbol.startsWith('W') && !feeToken
? feeTokenInfo.symbol.substring(1)
: feeTokenInfo.symbol
feeToken === ZeroAddress
? feeTokenInfo.symbol
: feeTokenInfo.symbol.startsWith('W')
? feeTokenInfo.symbol.substring(1)
: feeTokenInfo.symbol
Comment on lines +388 to +392
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

NIT: These chains of ternaries get pretty difficult to read, I think at this point good old if-elses would read easier.

throw new CCIPInsufficientBalanceError(
formatUnits(balance, feeTokenInfo.decimals),
formatUnits(fee, feeTokenInfo.decimals),
Expand Down
2 changes: 1 addition & 1 deletion ccip-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Format } from './commands/index.ts'
util.inspect.defaultOptions.depth = 6 // print down to tokenAmounts in requests
// generate:nofail
// `const VERSION = '${require('./package.json').version}-${require('child_process').execSync('git rev-parse --short HEAD').toString().trim()}'`
const VERSION = '1.2.1-df4b5e6'
const VERSION = '1.2.1-bda6deb'
// generate:end

const globalOpts = {
Expand Down
6 changes: 6 additions & 0 deletions ccip-cli/src/providers/sui.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CCIPArgumentInvalidError, bytesToBuffer } from '@chainlink/ccip-sdk/src/index.ts'
import { decodeSuiPrivateKey } from '@mysten/sui/cryptography'
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'

/**
Expand All @@ -9,6 +10,11 @@ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
export function loadSuiWallet({ wallet: walletOpt }: { wallet?: unknown }) {
if (typeof walletOpt !== 'string') throw new CCIPArgumentInvalidError('wallet', String(walletOpt))

if (walletOpt.startsWith('suiprivkey')) {
const { secretKey } = decodeSuiPrivateKey(walletOpt)
return Ed25519Keypair.fromSecretKey(secretKey)
}

const keyBytes = bytesToBuffer(walletOpt)
return Ed25519Keypair.fromSecretKey(keyBytes)
}
2 changes: 1 addition & 1 deletion ccip-sdk/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const DEFAULT_TIMEOUT_MS = 30000
/** SDK version string for telemetry header */
// generate:nofail
// `export const SDK_VERSION = '${require('./package.json').version}-${require('child_process').execSync('git rev-parse --short HEAD').toString().trim()}'`
export const SDK_VERSION = '1.2.1-df4b5e6'
export const SDK_VERSION = '1.2.1-bda6deb'
// generate:end

/** SDK telemetry header name */
Expand Down
10 changes: 6 additions & 4 deletions ccip-sdk/src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ export type ExecuteOpts = (
forceLookupTable?: boolean
}

/** Result of {@link Chain.resolveExecuteOpts}: execute options with a concrete `input` payload. */
export type ResolvedExecuteOpts = Extract<ExecuteOpts, { input: ExecutionInput }>

/**
* Works like an interface for a base Chain class, but provides implementation (which can be
* specialized) for some basic methods
Expand Down Expand Up @@ -1088,10 +1091,9 @@ export abstract class Chain<F extends ChainFamily = ChainFamily> {
*
* @throws {@link CCIPApiClientNotAvailableError} if `messageId` is provided but no apiClient
*/
protected async resolveExecuteOpts(
opts: ExecuteOpts,
): Promise<Extract<ExecuteOpts, { input: unknown }>> {
let opts_: Extract<typeof opts, { input: unknown }>
/** Resolves {@link ExecuteOpts} to a form that always includes `input` (fetches from API when using `messageId`). */
async resolveExecuteOpts(opts: ExecuteOpts): Promise<ResolvedExecuteOpts> {
let opts_: ResolvedExecuteOpts
if ('input' in opts) {
opts_ = opts
} else if (!this.apiClient) throw new CCIPApiClientNotAvailableError()
Expand Down
Loading
Loading