Skip to content

Bump Viem Versions for both packages on a develop branch + Change GH contribution guidelines #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
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
48 changes: 38 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@

[![License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/smartcontractkit/ccip-javascript-sdk/blob/main/LICENSE)
[![SDK Documentation](https://img.shields.io/static/v1?label=sdk-docs&message=latest&color=blue)](https://docs.chain.link/ccip/ccip-javascript-sdk/)

</div>

# CCIP JavaScript SDK

The CCIP JavaScript SDK includes two packages:
- [`ccip-js`](/packages/ccip-js/README.md): A TypeScript library that provides a client for managing cross-chain token transfers that use Chainlink's [Cross-Chain Interoperability Protocol (CCIP)](https://docs.chain.link/ccip) routers.
- [`ccip-react-components`](/packages/ccip-react-components/README.md): A set of prebuilt ready-to-use UI components built on top of `ccip-js`.
### Start here

The CCIP JavaScript SDK is a monorepo for two packages:

- [`ccip-js`](/packages/ccip-js/README.md): A TypeScript library that provides a client for managing cross-chain token transfers that use Chainlink's [Cross-Chain Interoperability Protocol (CCIP)](https://docs.chain.link/ccip) routers.
- [`ccip-react-components`](/packages/ccip-react-components/README.md): A set of prebuilt ready-to-use React UI components. This package depends on `ccip-js`.

Using both packages, you can add a fully featured CCIP bridge to your app that can be styled to match your app design.

To view more detailed documentation and more examples, visit the [Chainlink Javascript SDK Documentation](https://docs.chain.link/ccip/ccip-javascript-sdk/).
Using both packages, you can add a fully featured CCIP bridge to your app that can be styled to match your app design.

To view more detailed documentation and more examples, visit the [Chainlink Javascript SDK Documentation](https://docs.chain.link/ccip/ccip-javascript-sdk/). Development specific information is also found in individual READMEs inside the `./packages/<<PACKAGE_NAME>>` directory.

There is also an example implementation of a front end NextJS app that uses these packages in `./examples/nextjs`. That has its own README as well.

### Prerequisites

Expand All @@ -29,7 +35,6 @@ git clone https://github.com/smartcontractkit/ccip-javascript-sdk.git

3. Run `pnpm install`


### Run the example app

```sh
Expand All @@ -42,9 +47,12 @@ pnpm dev-example

### Build packages

If you want to make changes to the package code, you need to rebuild the packages and make sure package.json file to point to the updated local versions.
If you want to make changes to the package code, you need to rebuild the packages.
Then:

Make sure to build the `ccip-js` package before you build the `ccip-react-components` package. The React components depend on the JS package.
1. Make sure to build the `ccip-js` package before you build the `ccip-react-components` package. The React components depend on the JS package.

2. Make sure your client's package.json file to points to the updated local versions or use npm link or equivalent in your downstream client code. You can see examples of this in the steps below.

Follow these steps:

Expand Down Expand Up @@ -73,9 +81,29 @@ pnpm build-components
"@chainlink/ccip-react-components": "link:../../packages/ccip-react-components",
```

## Contributing

Contributions to either repos are welcome! Please open an issue or submit a pull request using the process below.

1. Fork the repository.
2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/ccip-javascript-sdk.git`
3. Navigate to directory: `cd ccip-javascript-sdk`
4. Fetch all branches: `git fetch origin`
5. Switch to develop branch: `git checkout develop`
6. Install dependencies: `pnpm install`
7. Create a feature branch: `git checkout -b feature/my-feature`
8. Commit your changes
9. Push to the branch (git push origin feature/my-feature).
10. Open a pull request from your fork to the `develop` branch of this repo.

🚨 Always branch off from `develop` when creating your feature branch.

## Resources

- [ccip-js README](./packages/ccip-js/README.md)
- [ccip-react-components README](./packages/ccip-react-components/README.md)
- [examples/nextjs README](./examples/nextjs/README.md)
- [Chainlink CCIP Javascript SDK Documentation](https://docs.chain.link/ccip/ccip-javascript-sdk/)
- [Chainlink CCIP Documentation](https://docs.chain.link/ccip)
- [Chainlink CCIP Directory](https://docs.chain.link/ccip/directory)
- [Chainlink Documentation](https://docs.chain.link/)
- [Chainlink Documentation](https://docs.chain.link/)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"build-components": "pnpm --filter ccip-react-components run build",
"dev-example": "pnpm --filter example-nextjs run dev",
"clean": "rm -rf node_modules packages/*/node_modules packages/*/dist examples/nextjs/node_modules examples/nextjs/.next",
"test-ccip-js": "pnpm --filter ccip-js run test",
"test-ccip-js": "pnpm --filter ccip-js run t:int",
"test-components": "pnpm --filter ccip-react-components run test"
},
"devDependencies": {
Expand Down
19 changes: 5 additions & 14 deletions packages/ccip-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,26 +605,17 @@ folder. From there, the relevant ABI arrays must be manually moved to `./src/abi

#### Running tests

1. cd into `packages/ccip-js` and then run `pnpm install` OR from the project root you can run `pnpm i -w`
1. Integration tests against testnets are favored in the ccip-js package.

2. open a new terminal window and run `foundryup` followed by `anvil` - requires that you've [installed Foundry Anvil](https://book.getfoundry.sh/anvil/).
<b?>Note:</b> that Anvil is only needed for the integrations tests inside `./test` which uses the [Chainlink Local](https://github.com/smartcontractkit/chainlink-local) simulator. Actual testnet and mainnet behavior may differ from time to time and passing these tests does not guarantee testnet or mainnet behavior.
2. Start by cd into `packages/ccip-js` and then run `pnpm install` OR from the project root you can run `pnpm i -w`

3. Back in the first terminal, inside, `packages/ccip-js` run `export PRIVATE_KEY=xxxxxx` to set your private key and then run `pnpm t:int` or `pnpm t:uint`.
3. Back in the first terminal, inside, `packages/ccip-js` run `export PRIVATE_KEY=0x.....` to set your private key and then run `pnpm t:int`. If you're in the entire repo's root you can run the workspace filtering command `pnpm test-ccip-js`

Note some tests are flaky - this is under investigation. You can choose to run just the mocked test or the testnet integration tests using `pnpm jest <<name of test file>>`.

Note further that we have set a 180000ms (3 mins) timeout on the jest config. This can cause the testnet integration test to "hang" for the entire duration.
Note further that we have set a 180000ms (3 mins) timeout on the jest config. This can cause the testnet integration test to appear to "hang"until timeout completes.

### Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

1. Fork the repository.
1. Create your feature branch (git checkout -b feature/my-feature).
1. Commit your changes (git commit -m 'Add some feature').
1. Push to the branch (git push origin feature/my-feature).
1. Open a pull request.
Please see the main README.

## License

Expand Down
166 changes: 166 additions & 0 deletions packages/ccip-js/artifacts-compile/SimpleCCIPReceiver.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions packages/ccip-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@chainlink/ccip-js",
"version": "0.2.4",
"version": "0.2.5",
"private": false,
"main": "dist/api.js",
"types": "dist/api.d.ts",
Expand All @@ -21,7 +21,7 @@
"devDependencies": {
"@babel/preset-typescript": "^7.26.0",
"@chainlink/contracts": "^1.3.0",
"@chainlink/contracts-ccip": "^1.5.0",
"@chainlink/contracts-ccip": "^1.6.0",
"@chainlink/env-enc": "^1.0.5",
"@chainlink/local": "^0.2.3",
"@jest/globals": "^29.7.0",
Expand Down Expand Up @@ -50,6 +50,6 @@
"mocha": "^11.1.0",
"ts-jest": "^29.2.5",
"typescript": "^5.8.2",
"viem": "2.21.25"
"viem": "^2.31.3"
}
}
38 changes: 32 additions & 6 deletions packages/ccip-js/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,31 @@ export const createClient = (): Client => {
return transferPoolTokenOutboundLimit as RateLimiterState
}

/*
* Internal only helper function to scale fee decimals for non-standard chains
* @param fee - The fee to scale
* @param chain - The chain to scale the fee for.
* @returns The scaled fee if the chain's native token does not use 18 decimals. Otherwise the original fee.
*/
function scaleFeeDecimals(fee: bigint, chain: Viem.Chain): bigint {
const scaleFactorForChain: Record<string, number> = { hedera: 10 }

const nonStandardChain = Object.keys(scaleFactorForChain).find((nonStandardChain) => {
return chain.name.toLowerCase().includes(nonStandardChain) // Tests that 'Hedera Testnet' includes "hedera"
})

if (nonStandardChain) {
const scaledFee = fee * BigInt(10 ** scaleFactorForChain[nonStandardChain])
console.info(
`scaleFeeDecimals(): Source chain '${chain.name}' has non-standard decimals. Scaling fee by ${scaleFactorForChain[nonStandardChain]} decimals`,
)
return scaledFee
}

// Unchanged.
return fee
}

async function getFee(options: Parameters<Client['getFee']>[0]) {
checkIsAddressValid(
options.routerAddress,
Expand Down Expand Up @@ -786,12 +811,15 @@ export const createClient = (): Client => {
}
}

return (await readContract(options.client, {
const fee = (await readContract(options.client, {
abi: RouterABI,
address: options.routerAddress,
functionName: 'getFee',
args: buildArgs(options),
})) as bigint

// Scale fee if needed based on chain
return scaleFeeDecimals(fee, options.client.chain!)
}

async function getTokenAdminRegistry(options: Parameters<Client['getTokenAdminRegistry']>[0]) {
Expand Down Expand Up @@ -859,16 +887,14 @@ export const createClient = (): Client => {
}
}

const writeContractParameters = {
const writeContractParameters: any = {
chain: options.client.chain,
abi: RouterABI,
address: options.routerAddress,
functionName: 'ccipSend',
args: buildArgs(options),
account: options.client.account!,
...(!options.feeTokenAddress && {
value: await getFee(options),
}),
value: options.feeTokenAddress ? undefined : await getFee(options), // Only add native token value if no fee token is specified
...options.writeContractParameters,
}

Expand All @@ -887,7 +913,7 @@ export const createClient = (): Client => {
functionName: 'typeAndVersion',
})

console.info('transferTokens(): CCIP type and version: ', typeAndVersion)
console.info('transferTokens(): CCIP onramp, typeAndVersion: ', onRamp, typeAndVersion)

const eventName = typeAndVersion === 'EVM2EVMOnRamp 1.5.0' ? 'CCIPSendRequested' : 'CCIPMessageSent'
const abi = typeAndVersion === 'EVM2EVMOnRamp 1.5.0' ? OnRampABI : OnRampABI_1_6
Expand Down
24 changes: 4 additions & 20 deletions packages/ccip-js/src/contracts/BridgeToken.sol
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {BurnMintERC677} from '@chainlink/contracts-ccip/src/v0.8/shared/token/ERC677/BurnMintERC677.sol';
// import {BurnMintERC677} from '/@chainlink/contracts-ccip/src/v0.8/shared/token/ERC677/BurnMintERC677.sol';
import {BurnMintERC677Helper} from '@chainlink/contracts-ccip/contracts/test/helpers/BurnMintERC677Helper.sol';

/// @title BridgeToken
/// @notice This contract extends the functionality of the BurnMintERC677 token contract to include a `drip` function that mints one full token to a specified address.
/// @notice This contract is the same as BurnMintERC677Helper in that it extends the functionality of the BurnMintERC677 token contract to include a `drip` function that mints one full token to a specified address.
/// @dev Inherits from the BurnMintERC677 contract and sets the token name, symbol, decimals, and initial supply in the constructor.
contract BridgeToken is BurnMintERC677 {
/**
* @notice Constructor to initialize the BridgeToken contract with a name and symbol.
* @dev Calls the parent constructor of BurnMintERC677 with fixed decimals (18) and initial supply (0).
* @param name - The name of the token.
* @param symbol - The symbol of the token.
*/
constructor(string memory name, string memory symbol) BurnMintERC677(name, symbol, 18, 0) {}

/**
* @notice Mints one full token (1e18) to the specified address.
* @dev Calls the internal `_mint` function from the BurnMintERC677 contract.
* @param to - The address to receive the minted token.
*/
function drip(address to) external {
_mint(to, 1e18);
}
}
abstract contract BridgeToken is BurnMintERC677Helper { }
2 changes: 1 addition & 1 deletion packages/ccip-js/src/contracts/EVM2EVMOffRamp.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {EVM2EVMOffRamp} from "@chainlink/contracts-ccip/src/v0.8/ccip/offRamp/EVM2EVMOffRamp.sol";
import {EVM2EVMOffRampHelper} from "@chainlink/contracts-ccip/contracts/test/helpers/EVM2EVMOffRampHelper.sol";
2 changes: 1 addition & 1 deletion packages/ccip-js/src/contracts/EVM2EVMOnRamp.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {EVM2EVMOnRamp} from '@chainlink/contracts-ccip/src/v0.8/ccip/onRamp/EVM2EVMOnRamp.sol';
import {OnRampHelper} from '@chainlink/contracts-ccip/contracts/test/helpers/OnRampHelper.sol';
8 changes: 4 additions & 4 deletions packages/ccip-js/src/contracts/FeeQuoter.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IFeeQuoter} from '@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IFeeQuoter.sol';
import {OwnerIsCreator} from '@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol';
import {Internal} from '@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Internal.sol';
import {USDPriceWith18Decimals} from '@chainlink/contracts-ccip/src/v0.8/ccip/libraries/USDPriceWith18Decimals.sol';
import {IFeeQuoter} from '@chainlink/contracts-ccip/contracts/interfaces/IFeeQuoter.sol';
import {OwnerIsCreator} from '@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol';
import {Internal} from '@chainlink/contracts-ccip/contracts/libraries/Internal.sol';
import {USDPriceWith18Decimals} from '@chainlink/contracts-ccip/contracts/libraries/USDPriceWith18Decimals.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';

// import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.0/utils/structs/EnumerableSet.sol";
Expand Down
2 changes: 1 addition & 1 deletion packages/ccip-js/src/contracts/Router.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;
import { Router } from "@chainlink/contracts-ccip/src/v0.8/ccip/Router.sol";
import { Router } from "@chainlink/contracts-ccip/contracts/Router.sol";

// import {
// ITypeAndVersion,
Expand Down
57 changes: 57 additions & 0 deletions packages/ccip-js/src/contracts/SimpleCCIPReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.24;

import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
import {CCIPReceiver} from "@chainlink/contracts-ccip/contracts/applications/CCIPReceiver.sol";

/**
* THIS IS AN EXAMPLE CONTRACT THAT IS USED IN THIS SDK FOR INTEGRATION TESTING.
* IT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/

/// @title - A simple contract for receiving string data across chains.
contract SimpleCCIPReceiver is CCIPReceiver {
// Event emitted when a message is received from another chain.
event MessageReceived(
bytes32 indexed messageId, // The unique ID of the message.
uint64 indexed sourceChainSelector, // The chain selector of the source chain.
address sender, // The address of the sender from the source chain.
string text // The text that was received.
);

bytes32 private s_lastReceivedMessageId; // Store the last received messageId.
string private s_lastReceivedText; // Store the last received text.

/// @notice Constructor initializes the contract with the router address.
/// @param router The address of the router contract.
constructor(address router) CCIPReceiver(router) {}

/// handle a received message
function _ccipReceive(
Client.Any2EVMMessage memory any2EvmMessage
) internal override {
s_lastReceivedMessageId = any2EvmMessage.messageId; // fetch the messageId
s_lastReceivedText = abi.decode(any2EvmMessage.data, (string)); // abi-decoding of the sent text

emit MessageReceived(
any2EvmMessage.messageId,
any2EvmMessage.sourceChainSelector, // fetch the source chain identifier (aka selector)
abi.decode(any2EvmMessage.sender, (address)), // abi-decoding of the sender address,
abi.decode(any2EvmMessage.data, (string))
);
}

/// @notice Fetches the details of the last received message.
/// @return messageId The ID of the last received message.
/// @return text The last received text.
function getLastReceivedMessageDetails()
external
view
returns (bytes32 messageId, string memory text)
{
return (s_lastReceivedMessageId, s_lastReceivedText);
}
}
4 changes: 0 additions & 4 deletions packages/ccip-js/src/contracts/TokenProxy.sol

This file was deleted.

Loading