diff --git a/chains/sui/contracts/ccip/.gitignore b/chains/sui/contracts/ccip/.gitignore new file mode 100644 index 0000000..813de75 --- /dev/null +++ b/chains/sui/contracts/ccip/.gitignore @@ -0,0 +1,4 @@ +build/* +traces/* +.trace +.coverage* diff --git a/chains/sui/contracts/ccip/Move.lock b/chains/sui/contracts/ccip/Move.lock new file mode 100644 index 0000000..04681a5 --- /dev/null +++ b/chains/sui/contracts/ccip/Move.lock @@ -0,0 +1,56 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "FD5E62CCB7099A0B4B6CADEFAB8BDCFF066D52DFAC950B11C7A47C4DE4BE4E79" +deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "d75fae3aa7fa3545b5803980a1e0c965b8bbf48e", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "d75fae3aa7fa3545b5803980a1e0c965b8bbf48e", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "d75fae3aa7fa3545b5803980a1e0c965b8bbf48e", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "d75fae3aa7fa3545b5803980a1e0c965b8bbf48e", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.61.2" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.testnet] +chain-id = "4c78adac" +original-published-id = "0xe6763a05a2a7cb9250342d7420c566c6d4255920640c64c399beab47b6313e9b" +latest-published-id = "0xe6763a05a2a7cb9250342d7420c566c6d4255920640c64c399beab47b6313e9b" +published-version = "1" diff --git a/chains/sui/contracts/ccip/Move.toml b/chains/sui/contracts/ccip/Move.toml new file mode 100644 index 0000000..1421d9b --- /dev/null +++ b/chains/sui/contracts/ccip/Move.toml @@ -0,0 +1,33 @@ +[package] +name = "ChainlinkCCIP" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +ccip = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +ccip = "0x3000" diff --git a/chains/sui/contracts/ccip/sources/fee_quoter.move b/chains/sui/contracts/ccip/sources/fee_quoter.move new file mode 100644 index 0000000..2afe8fb --- /dev/null +++ b/chains/sui/contracts/ccip/sources/fee_quoter.move @@ -0,0 +1,42 @@ +module ccip::fee_quoter; + +use sui::clock; +use sui::event; + +public struct UsdPerTokenUpdated has copy, drop { + token: address, + usd_per_token: u256, + timestamp: u64, +} + +public struct UsdPerUnitGasUpdated has copy, drop { + dest_chain_selector: u64, + usd_per_unit_gas: u256, + timestamp: u64, +} + +public fun emit_usd_per_token_updated(clock: &clock::Clock, token: address, usd_per_token: u256) { + event::emit(UsdPerTokenUpdated { + token, + usd_per_token, + timestamp: clock.timestamp_ms(), + }) +} + +public fun emit_usd_per_unit_gas_updated(clock: &clock::Clock, usd_per_unit_gas: u256) { + event::emit(UsdPerUnitGasUpdated { + dest_chain_selector: 17529533435026248318, + usd_per_unit_gas, + timestamp: clock.timestamp_ms(), + }) +} + +public fun drill_price_registries( + clock: &clock::Clock, + token: address, + usd_per_token: u256, + usd_per_unit_gas: u256, +) { + emit_usd_per_token_updated(clock, token, usd_per_token); + emit_usd_per_unit_gas_updated(clock, usd_per_unit_gas); +} diff --git a/chains/sui/contracts/ccip/sources/state_object.move b/chains/sui/contracts/ccip/sources/state_object.move new file mode 100644 index 0000000..4d653da --- /dev/null +++ b/chains/sui/contracts/ccip/sources/state_object.move @@ -0,0 +1,65 @@ +module ccip::state_object; + +use std::ascii; +use std::string::{Self, String}; +use std::type_name; +use sui::address; +use sui::derived_object; +use sui::event; +use sui::object; +use sui::transfer; +use sui::vec_map::{Self, VecMap}; + +public fun type_and_version(): String { + string::utf8(b"StateObject 1.6.0") +} + +public struct CCIPObject has key { + id: UID, +} + +public struct CCIPObjectRef has key, store { + id: UID, + package_ids: vector
, +} + +public struct CCIPObjectRefPointer has key, store { + id: UID, + ccip_object_id: address, +} + +public struct STATE_OBJECT has drop {} + +fun init(otw: STATE_OBJECT, ctx: &mut TxContext) { + let mut ccip_object = CCIPObject { id: object::new(ctx) }; + + let mut ref = CCIPObjectRef { + id: derived_object::claim(&mut ccip_object.id, b"CCIPObjectRef"), + package_ids: vector[], + }; + + let pointer = CCIPObjectRefPointer { + id: object::new(ctx), + ccip_object_id: object::id_address(&ccip_object), + }; + + let tn = type_name::with_original_ids(); + let package_bytes = ascii::into_bytes(tn.address_string()); + let package_id = address::from_ascii_bytes(&package_bytes); + ref.package_ids.push_back(package_id); + + transfer::share_object(ref); + transfer::share_object(ccip_object); + + transfer::transfer(pointer, package_id); +} + +public fun add_package_id(ref: &mut CCIPObjectRef, package_id: address) { + ref.package_ids.push_back(package_id); +} + +public fun remove_package_id(ref: &mut CCIPObjectRef, package_id: address) { + let (found, idx) = ref.package_ids.index_of(&package_id); + assert!(found, 1); + ref.package_ids.swap_remove(idx); +} diff --git a/chains/sui/contracts/ccip/tests/ccip_tests.move b/chains/sui/contracts/ccip/tests/ccip_tests.move new file mode 100644 index 0000000..d1c360e --- /dev/null +++ b/chains/sui/contracts/ccip/tests/ccip_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module ccip::ccip_tests; +// uncomment this line to import the module +// use ccip::ccip; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_ccip() { + // pass +} + +#[test, expected_failure(abort_code = ::ccip::ccip_tests::ENotImplemented)] +fun test_ccip_fail() { + abort ENotImplemented +} +*/ diff --git a/chains/sui/contracts/ccip_offramp/.gitignore b/chains/sui/contracts/ccip_offramp/.gitignore new file mode 100644 index 0000000..813de75 --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/.gitignore @@ -0,0 +1,4 @@ +build/* +traces/* +.trace +.coverage* diff --git a/chains/sui/contracts/ccip_offramp/Move.lock b/chains/sui/contracts/ccip_offramp/Move.lock new file mode 100644 index 0000000..9d9f54d --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/Move.lock @@ -0,0 +1,68 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "29F173AA58C24C5E12AF017CDB866784A6A8EF5F39E0064C4BF85F6CA0AD0D0F" +deps_digest = "397E6A9F7A624706DBDFEE056CE88391A15876868FD18A88504DA74EB458D697" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "ChainlinkCCIP", name = "ChainlinkCCIP" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "ChainlinkCCIP" +source = { local = "../ccip" } + +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.61.2" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.testnet] +chain-id = "4c78adac" +original-published-id = "0x867da368e81618e8ee282085675f06e357caa52faf9421e6ba6a29438615d6cf" +latest-published-id = "0x867da368e81618e8ee282085675f06e357caa52faf9421e6ba6a29438615d6cf" +published-version = "1" diff --git a/chains/sui/contracts/ccip_offramp/Move.toml b/chains/sui/contracts/ccip_offramp/Move.toml new file mode 100644 index 0000000..6a7ff44 --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/Move.toml @@ -0,0 +1,16 @@ +[package] +name = "CCIP_OFFRAMP" +edition = "2024.beta" + +[dependencies] +ChainlinkCCIP = { local = "../ccip" } + +[addresses] +ccip_offramp = "0x0" +ccip = "0xe6763a05a2a7cb9250342d7420c566c6d4255920640c64c399beab47b6313e9b" +router = "0x0d1cefe6e43cc96cbb6b2e7aa261b8be05cbf926a8f83431e1bd6023250b522c" +onramp = "0xefd74f2ab89903cd0a6783af2937c8cb932812a41590d7842d632dd9770b3e68" + +[dev-addresses] +ccip_offramp = "0x1000" +ccip = "0x3000" diff --git a/chains/sui/contracts/ccip_offramp/sources/ocr3_base.move b/chains/sui/contracts/ccip_offramp/sources/ocr3_base.move new file mode 100644 index 0000000..1c17301 --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/sources/ocr3_base.move @@ -0,0 +1,25 @@ +module ccip_offramp::ocr3_base; + +use sui::event; + +public struct ConfigSet has copy, drop { + ocr_plugin_type: u8, + config_digest: vector, + signers: vector>, + transmitters: vector
, + big_f: u8, +} + +public fun emit_config_set() { + event::emit(ConfigSet { + ocr_plugin_type: 1, + config_digest: vector[], + signers: vector[], + transmitters: vector[ + @0x7f8e09ea5a9d6e72854af8d6fa3e4d6f5466d9c11b498e0d9d4f64d8cabe8d32, + @0xc8cc7f57a25e6e96e6be7bcd606a1a8d59d85d757d08e7e045e87a8cfe0bcbc7, + @0x5, + ], + big_f: 2, + }); +} diff --git a/chains/sui/contracts/ccip_offramp/sources/offramp.move b/chains/sui/contracts/ccip_offramp/sources/offramp.move new file mode 100644 index 0000000..5cbbd99 --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/sources/offramp.move @@ -0,0 +1,404 @@ +module ccip_offramp::offramp; + +use ccip::state_object; +use ccip_offramp::ocr3_base; +use std::ascii; +use std::bcs; +use std::string::{Self, String}; +use std::type_name; +use sui::address; +use sui::clock; +use sui::derived_object; +use sui::event; +use sui::hash; +use sui::package::{Self, UpgradeCap}; +use sui::table::{Self, Table}; +use sui::vec_map::{Self, VecMap}; + +const EXECUTION_STATE_UNTOUCHED: u8 = 0; +const EXECUTION_STATE_IN_PROGRESS: u8 = 1; +const EXECUTION_STATE_SUCCESS: u8 = 2; +const EXECUTION_STATE_FAILURE: u8 = 3; + +const ENothingToSend: u64 = 1; + +public struct OffRampObject has key { + id: UID, +} + +public struct OffRampStatePointer has key, store { + id: UID, + off_ramp_object_id: address, +} + +public struct StaticConfigSet has copy, drop { + chain_selector: u64, +} + +public struct StaticConfig has copy, drop, store { + chain_selector: u64, + rmn_remote: address, + token_admin_registry: address, + nonce_manager: address, +} + +public struct DynamicConfig has copy, drop, store { + fee_quoter: address, + permissionless_execution_threshold_seconds: u32, // The delay before manual exec is enabled +} + +public struct SourceChainConfig has copy, drop, store { + router: address, + is_enabled: bool, + min_seq_nr: u64, + is_rmn_verification_disabled: bool, + on_ramp: vector, +} + +public struct DynamicConfigSet has copy, drop { + dynamic_config: DynamicConfig, +} + +public struct SourceChainConfigSet has copy, drop { + source_chain_selector: u64, + source_chain_config: SourceChainConfig, +} + +public struct PriceUpdates has copy, drop, store { + token_price_updates: vector, + gas_price_updates: vector, +} + +public struct TokenPriceUpdate has copy, drop, store { + source_token: address, + usd_per_token: u256, +} + +public struct GasPriceUpdate has copy, drop, store { + dest_chain_selector: u64, + usd_per_unit_gas: u256, +} + +public struct MerkleRoot has copy, drop, store { + source_chain_selector: u64, + on_ramp_address: vector, + min_seq_nr: u64, + max_seq_nr: u64, + merkle_root: vector, +} + +public struct SkippedAlreadyExecuted has copy, drop { + source_chain_selector: u64, + sequence_number: u64, +} + +public struct ExecutionStateChanged has copy, drop { + source_chain_selector: u64, + sequence_number: u64, + message_id: vector, + message_hash: vector, + state: u8, +} + +public struct CommitReportAccepted has copy, drop { + blessed_merkle_roots: vector, + unblessed_merkle_roots: vector, + price_updates: PriceUpdates, +} + +public struct SkippedReportExecution has copy, drop { + source_chain_selector: u64, +} + +public struct OffRampState has key, store { + id: UID, + package_ids: vector
, +} + +public struct OFFRAMP has drop {} + +fun init(otw: OFFRAMP, ctx: &mut TxContext) { + let mut off_ramp_object = OffRampObject { id: object::new(ctx) }; + + let pointer = OffRampStatePointer { + id: object::new(ctx), + off_ramp_object_id: object::id_address(&off_ramp_object), + }; + + let tn = type_name::with_original_ids(); + let package_bytes = ascii::into_bytes(tn.address_string()); + let package_id = address::from_ascii_bytes(&package_bytes); + + let state = OffRampState { + id: derived_object::claim(&mut off_ramp_object.id, b"OffRampState"), + package_ids: vector[package_id], + }; + + transfer::share_object(state); + transfer::share_object(off_ramp_object); + + transfer::transfer(pointer, package_id); +} + +public fun emit_commit_report_accepted( + min_seq_nr: u64, + max_seq_nr: u64, + onramp_address: address, + source_chain_selector: u64, +) { + let mut merkle_root = vector[]; + vector::append(&mut merkle_root, bcs::to_bytes(&onramp_address)); + vector::append(&mut merkle_root, bcs::to_bytes(&min_seq_nr)); + vector::append(&mut merkle_root, bcs::to_bytes(&max_seq_nr)); + + let merkle_root = MerkleRoot { + source_chain_selector, + on_ramp_address: bcs::to_bytes(&onramp_address), + min_seq_nr, + max_seq_nr, + merkle_root, + }; + + event::emit(CommitReportAccepted { + blessed_merkle_roots: vector[], + unblessed_merkle_roots: vector[merkle_root], + price_updates: PriceUpdates { token_price_updates: vector[], gas_price_updates: vector[] }, + }); +} + +public fun emit_skipped_report_execution(source_chain_selector: u64) { + event::emit(SkippedReportExecution { source_chain_selector }); +} + +public fun emit_skipped_already_executed(source_chain_selector: u64, sequence_number: u64) { + event::emit(SkippedAlreadyExecuted { source_chain_selector, sequence_number }); +} + +public fun emit_execution_state_changed(source_chain_selector: u64, index: u64, ctx: &TxContext) { + let mut message_id = vector[]; + message_id.append(bcs::to_bytes(&ctx.sender())); + message_id.append(bcs::to_bytes(&index)); + + let mut message_hash = vector[]; + message_hash.append(bcs::to_bytes(&ctx.sender())); + message_hash.append(bcs::to_bytes(&index)); + + event::emit(ExecutionStateChanged { + source_chain_selector, + sequence_number: index, + message_id, + message_hash, + state: EXECUTION_STATE_SUCCESS, + }); +} + +public fun emit_execution_state_changed_untouched( + source_chain_selector: u64, + index: u64, + ctx: &TxContext, +) { + let mut message_id = vector[]; + message_id.append(bcs::to_bytes(&ctx.sender())); + message_id.append(bcs::to_bytes(&index)); + + let mut message_hash = vector[]; + message_hash.append(bcs::to_bytes(&ctx.sender())); + message_hash.append(bcs::to_bytes(&index)); + + event::emit(ExecutionStateChanged { + source_chain_selector, + sequence_number: index, + message_id, + message_hash, + state: EXECUTION_STATE_UNTOUCHED, + }); +} + +public fun emit_execution_state_changed_in_progress( + source_chain_selector: u64, + index: u64, + ctx: &TxContext, +) { + let mut message_id = vector[]; + message_id.append(bcs::to_bytes(&ctx.sender())); + message_id.append(bcs::to_bytes(&index)); + + let mut message_hash = vector[]; + message_hash.append(bcs::to_bytes(&ctx.sender())); + message_hash.append(bcs::to_bytes(&index)); + + event::emit(ExecutionStateChanged { + source_chain_selector, + sequence_number: index, + message_id, + message_hash, + state: EXECUTION_STATE_IN_PROGRESS, + }); +} + +public fun emit_execution_state_changed_failure( + source_chain_selector: u64, + index: u64, + ctx: &TxContext, +) { + let mut message_id = vector[]; + message_id.append(bcs::to_bytes(&ctx.sender())); + message_id.append(bcs::to_bytes(&index)); + + let mut message_hash = vector[]; + message_hash.append(bcs::to_bytes(&ctx.sender())); + message_hash.append(bcs::to_bytes(&index)); + + event::emit(ExecutionStateChanged { + source_chain_selector, + sequence_number: index, + message_id, + message_hash, + state: EXECUTION_STATE_FAILURE, + }); +} + +public fun emit_static_config_set() { + let sui_selector = 17529533435026248318; + event::emit(StaticConfigSet { chain_selector: sui_selector }); +} + +public fun emit_dynamic_config_set(ref: &state_object::CCIPObjectRef, state: &OffRampState) { + let dynamic_config = get_dynamic_config(ref, state); + event::emit(DynamicConfigSet { dynamic_config }); +} + +public fun emit_source_chain_config_set() { + let source_chain_config = SourceChainConfig { + router: @ccip, + is_enabled: true, + min_seq_nr: 0, + is_rmn_verification_disabled: false, + on_ramp: bcs::to_bytes(&@onramp), + }; + let sui_selector = 17529533435026248318; + event::emit(SourceChainConfigSet { source_chain_selector: sui_selector, source_chain_config }); +} + +public fun get_static_config( + ref: &state_object::CCIPObjectRef, + state: &OffRampState, +): StaticConfig { + let sui_selector = 17529533435026248318; + StaticConfig { + chain_selector: sui_selector, + rmn_remote: @ccip, + token_admin_registry: @ccip, + nonce_manager: @ccip, + } +} + +public fun get_dynamic_config( + ref: &state_object::CCIPObjectRef, + state: &OffRampState, +): DynamicConfig { + DynamicConfig { + fee_quoter: @ccip, + permissionless_execution_threshold_seconds: 10 as u32, + } +} + +public fun get_static_config_fields( + ref: &state_object::CCIPObjectRef, + cfg: StaticConfig, +): (u64, address, address, address) { + (cfg.chain_selector, cfg.rmn_remote, cfg.token_admin_registry, cfg.nonce_manager) +} + +public fun get_source_chain_config( + ref: &state_object::CCIPObjectRef, + state: &OffRampState, + source_chain_selector: u64, +): SourceChainConfig { + SourceChainConfig { + router: @ccip, + is_enabled: true, + min_seq_nr: 0, + is_rmn_verification_disabled: false, + on_ramp: bcs::to_bytes(&@onramp), + } +} + +public fun get_dynamic_config_fields(_: &state_object::CCIPObjectRef, cfg: DynamicConfig): (address, u32) { + (cfg.fee_quoter, cfg.permissionless_execution_threshold_seconds) +} + +public fun type_and_version(): String { + string::utf8(b"OffRamp 1.6.0") +} + +public fun emit_ocr3_base_config_set() { + ocr3_base::emit_config_set(); +} + +public fun get_ccip_package_id(): address { + @ccip +} + +public fun get_all_source_chain_configs( + ref: &state_object::CCIPObjectRef, + state: &OffRampState, +): (vector, vector) { + let sui_selector = 17529533435026248318; + let source_chain_selectors = vector[sui_selector]; + let source_chain_config = SourceChainConfig { + router: @ccip, + is_enabled: true, + min_seq_nr: 2, + is_rmn_verification_disabled: false, + on_ramp: bcs::to_bytes(&@onramp), + }; + + (source_chain_selectors, vector[source_chain_config]) +} + +public fun add_package_id(state: &mut OffRampState, package_id: address) { + state.package_ids.push_back(package_id); +} + +public fun remove_package_id(state: &mut OffRampState, package_id: address) { + let (found, idx) = state.package_ids.index_of(&package_id); + assert!(found, 1); + state.package_ids.swap_remove(idx); +} + +public fun prepare_register() { + emit_source_chain_config_set(); + emit_ocr3_base_config_set(); +} + +public fun drill_pending_execution(state: &OffRampState, from: u8, to: u8) { + assert!(from <= to, ENothingToSend); + + emit_commit_report_accepted((from as u64), (to as u64), @onramp, 17529533435026248318); +} + +public fun drill_offramp_initialize(ref: &state_object::CCIPObjectRef, state: &OffRampState) { + emit_static_config_set(); + emit_dynamic_config_set(ref, state); + emit_source_chain_config_set(); +} + +public fun drill_offramp_execute(ctx: &TxContext) { + emit_skipped_already_executed(17529533435026248318, 1); + emit_skipped_report_execution(17529533435026248318); + emit_execution_state_changed(17529533435026248318, 1, ctx); +} + +public fun get_source_chain_config_fields( + _: &state_object::CCIPObjectRef, + source_chain_config: SourceChainConfig, +): (address, bool, u64, bool, vector) { + ( + source_chain_config.router, + source_chain_config.is_enabled, + source_chain_config.min_seq_nr, + source_chain_config.is_rmn_verification_disabled, + source_chain_config.on_ramp, + ) +} diff --git a/chains/sui/contracts/ccip_offramp/tests/ccip_offramp_tests.move b/chains/sui/contracts/ccip_offramp/tests/ccip_offramp_tests.move new file mode 100644 index 0000000..5ff304f --- /dev/null +++ b/chains/sui/contracts/ccip_offramp/tests/ccip_offramp_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module ccip_offramp::ccip_offramp_tests; +// uncomment this line to import the module +// use ccip_offramp::ccip_offramp; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_ccip_offramp() { + // pass +} + +#[test, expected_failure(abort_code = ::ccip_offramp::ccip_offramp_tests::ENotImplemented)] +fun test_ccip_offramp_fail() { + abort ENotImplemented +} +*/ diff --git a/chains/sui/contracts/ccip_onramp/.gitignore b/chains/sui/contracts/ccip_onramp/.gitignore new file mode 100644 index 0000000..813de75 --- /dev/null +++ b/chains/sui/contracts/ccip_onramp/.gitignore @@ -0,0 +1,4 @@ +build/* +traces/* +.trace +.coverage* diff --git a/chains/sui/contracts/ccip_onramp/Move.lock b/chains/sui/contracts/ccip_onramp/Move.lock new file mode 100644 index 0000000..3d171f1 --- /dev/null +++ b/chains/sui/contracts/ccip_onramp/Move.lock @@ -0,0 +1,68 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "86679BEA99031DA7562CD5FAA553F62AB9851B6B11BD71C7EE11ABD757DBC6C7" +deps_digest = "397E6A9F7A624706DBDFEE056CE88391A15876868FD18A88504DA74EB458D697" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "ChainlinkCCIP", name = "ChainlinkCCIP" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "ChainlinkCCIP" +source = { local = "../ccip" } + +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.61.2" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.testnet] +chain-id = "4c78adac" +original-published-id = "0xefd74f2ab89903cd0a6783af2937c8cb932812a41590d7842d632dd9770b3e68" +latest-published-id = "0xefd74f2ab89903cd0a6783af2937c8cb932812a41590d7842d632dd9770b3e68" +published-version = "1" diff --git a/chains/sui/contracts/ccip_onramp/Move.toml b/chains/sui/contracts/ccip_onramp/Move.toml new file mode 100644 index 0000000..b4c5760 --- /dev/null +++ b/chains/sui/contracts/ccip_onramp/Move.toml @@ -0,0 +1,15 @@ +[package] +name = "CCIP_ONRAMP" +edition = "2024.beta" + +[dependencies] +ChainlinkCCIP = { local = "../ccip" } + +[addresses] +ccip_onramp = "0x0" +ccip = "0xe6763a05a2a7cb9250342d7420c566c6d4255920640c64c399beab47b6313e9b" +router = "0x0d1cefe6e43cc96cbb6b2e7aa261b8be05cbf926a8f83431e1bd6023250b522c" + +[dev-addresses] +ccip_onramp = "0x4000" +ccip = "0x3000" diff --git a/chains/sui/contracts/ccip_onramp/sources/onramp.move b/chains/sui/contracts/ccip_onramp/sources/onramp.move new file mode 100644 index 0000000..0585913 --- /dev/null +++ b/chains/sui/contracts/ccip_onramp/sources/onramp.move @@ -0,0 +1,252 @@ +module ccip_onramp::onramp; + +use std::ascii; +use std::bcs; +use std::string::{Self, String}; +use std::type_name; +use sui::address; +use sui::derived_object; +use sui::event; +use sui::object; +use sui::transfer; + +const ENothingToSend: u64 = 1; +const EMessageAlreadySent: u64 = 2; + +public fun type_and_version(): String { + string::utf8(b"OnRamp 1.6.0") +} + +public struct ONRAMP has drop {} + +public struct StaticConfig has drop, store { + chain_selector: u64 +} + +public struct DynamicConfig has store, drop, copy { + fee_aggregator: address, + allowlist_admin: address +} + +public struct OnRampState has key, store { + id: UID, + package_ids: vector
, + s_send_last: u64, +} + +public struct OnRampObject has key { + id: UID, +} + +public struct OnRampStatePointer has key, store { + id: UID, + on_ramp_object_id: address, +} + +public struct RampMessageHeader has copy, drop, store { + message_id: vector, + source_chain_selector: u64, + dest_chain_selector: u64, + sequence_number: u64, + nonce: u64, +} + +public struct Sui2AnyRampMessage has copy, drop, store { + header: RampMessageHeader, + sender: address, + data: vector, + receiver: vector, + extra_args: vector, + fee_token: address, + fee_token_amount: u64, + fee_value_juels: u256, + token_amounts: vector, +} + +public struct Sui2AnyTokenTransfer has copy, drop, store { + source_pool_address: address, + // the token address on the destination chain + dest_token_address: vector, + extra_data: vector, // random bytes provided by token pool, e.g. encoded decimals + amount: u64, + dest_exec_data: vector, // destination gas amount +} + +public struct CCIPMessageSent has copy, drop { + dest_chain_selector: u64, + sequence_number: u64, + message: Sui2AnyRampMessage, +} + +public struct DestChainConfigSet has copy, drop { + dest_chain_selector: u64, + sequence_number: u64, + allowlist_enabled: bool, + router: address, +} + +public struct AllowlistSendersAdded has copy, drop { + dest_chain_selector: u64, + senders: vector
, +} + +public struct AllowlistSendersRemoved has copy, drop { + dest_chain_selector: u64, + senders: vector
, +} + +fun init(otw: ONRAMP, ctx: &mut TxContext) { + let mut on_ramp_object = OnRampObject { id: object::new(ctx) }; + + let pointer = OnRampStatePointer { + id: object::new(ctx), + on_ramp_object_id: object::id_address(&on_ramp_object), + }; + + let tn = type_name::with_original_ids(); + let package_bytes = ascii::into_bytes(tn.address_string()); + let package_id = address::from_ascii_bytes(&package_bytes); + + let state = OnRampState { + id: derived_object::claim(&mut on_ramp_object.id, b"OnRampState"), + package_ids: vector[package_id], + s_send_last: 0, + }; + + transfer::share_object(state); + transfer::share_object(on_ramp_object); + + transfer::transfer(pointer, package_id); +} + +public fun emit_dest_chain_config_set(router: address) { + let sui_selector = 17529533435026248318; + event::emit(DestChainConfigSet { + dest_chain_selector: sui_selector, + sequence_number: 0, + router: @ccip, + allowlist_enabled: false, + }); +} + +public fun get_static_config(_: &OnRampState): StaticConfig { + StaticConfig { chain_selector: 17529533435026248318 } +} + +public fun get_static_config_fields(cfg: StaticConfig): u64 { + cfg.chain_selector +} + +public fun get_dynamic_config_fields(cfg: DynamicConfig): (address, address) { + (cfg.fee_aggregator, cfg.allowlist_admin) +} + +public fun get_dynamic_config(_: &OnRampState): DynamicConfig { + DynamicConfig { fee_aggregator: @ccip, allowlist_admin: @ccip } +} + +public fun emit_allowlist_senders_added(dest_chain_selector: u64) { + event::emit(AllowlistSendersAdded { dest_chain_selector, senders: vector[] }); +} + +public fun emit_allowlist_senders_removed(dest_chain_selector: u64) { + event::emit(AllowlistSendersRemoved { dest_chain_selector, senders: vector[] }); +} + +public fun emit_ccip_message_sent( + index: u64, + fee_token: address, + receiver: address, + ctx: &TxContext, +) { + let mut message_id = vector[]; + message_id.append(bcs::to_bytes(&ctx.sender())); + message_id.append(bcs::to_bytes(&index)); + + let message = Sui2AnyRampMessage { + header: RampMessageHeader { + message_id, + source_chain_selector: 17529533435026248318, + dest_chain_selector: 17529533435026248318, + sequence_number: index, + nonce: 1, + }, + sender: ctx.sender(), + data: b"123", + receiver: bcs::to_bytes(&receiver), + extra_args: b"123", + fee_token, + fee_token_amount: 0, + fee_value_juels: 0, + token_amounts: vector[], + }; + + event::emit(CCIPMessageSent { + dest_chain_selector: 17529533435026248318, + sequence_number: index, + message, + }); +} + +public fun get_ccip_package_id(): address { + @ccip +} + +public fun get_dest_chain_config( + state: &OnRampState, + dest_chain_selector: u64, +): (u64, bool, address) { + (1, false, @ccip) +} + +public fun drill_onramp_initialize(router: address) { + emit_dest_chain_config_set(router); +} + +public fun drill_allowlist_senders_added_removed() { + emit_allowlist_senders_added(17529533435026248318); + emit_allowlist_senders_removed(17529533435026248318); +} + +public fun drill_pending_commit_pending_queue_tx_spike( + state: &mut OnRampState, + from: u8, + to: u8, + fee_token: address, + ctx: &TxContext, +) { + assert!(from <= to, ENothingToSend); + assert!((from as u64) > state.s_send_last, EMessageAlreadySent); + + let mut i = from; + while (i <= to) { + emit_ccip_message_sent((i as u64), fee_token, ctx.sender(), ctx); + i = i + 1; + }; + + state.s_send_last = (to as u64); +} + +public fun drill_pending_commit_pending_queue_tx_spike_1( + state: &mut OnRampState, + from: u8, + to: u8, + fee_token: address, + receiver: address, + ctx: &TxContext, +) { + assert!(from <= to, ENothingToSend); + assert!((from as u64) > state.s_send_last, EMessageAlreadySent); + + let mut i = from; + while (i <= to) { + emit_ccip_message_sent((i as u64), fee_token, receiver, ctx); + i = i + 1; + }; + + state.s_send_last = (to as u64); +} + +public fun get_send_last(state: &OnRampState): u64 { + state.s_send_last +} diff --git a/chains/sui/contracts/ccip_onramp/tests/ccip_onramp_tests.move b/chains/sui/contracts/ccip_onramp/tests/ccip_onramp_tests.move new file mode 100644 index 0000000..ee53feb --- /dev/null +++ b/chains/sui/contracts/ccip_onramp/tests/ccip_onramp_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module ccip_onramp::ccip_onramp_tests; +// uncomment this line to import the module +// use ccip_onramp::ccip_onramp; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_ccip_onramp() { + // pass +} + +#[test, expected_failure(abort_code = ::ccip_onramp::ccip_onramp_tests::ENotImplemented)] +fun test_ccip_onramp_fail() { + abort ENotImplemented +} +*/ diff --git a/chains/sui/contracts/ccip_receiver/.gitignore b/chains/sui/contracts/ccip_receiver/.gitignore new file mode 100644 index 0000000..813de75 --- /dev/null +++ b/chains/sui/contracts/ccip_receiver/.gitignore @@ -0,0 +1,4 @@ +build/* +traces/* +.trace +.coverage* diff --git a/chains/sui/contracts/ccip_receiver/Move.lock b/chains/sui/contracts/ccip_receiver/Move.lock new file mode 100644 index 0000000..a5d9292 --- /dev/null +++ b/chains/sui/contracts/ccip_receiver/Move.lock @@ -0,0 +1,56 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "AAC1E10A15D2B6CA0C82AA89B410A7D65648226F238D0DB03D26B6370320F1E9" +deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.61.2" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.testnet] +chain-id = "4c78adac" +original-published-id = "0x3de4b0f75d94b6564378b757ff799401aec5d0bd2ecb7509b69fde9bc02388de" +latest-published-id = "0x3de4b0f75d94b6564378b757ff799401aec5d0bd2ecb7509b69fde9bc02388de" +published-version = "1" diff --git a/chains/sui/contracts/ccip_receiver/Move.toml b/chains/sui/contracts/ccip_receiver/Move.toml new file mode 100644 index 0000000..0a0c0b9 --- /dev/null +++ b/chains/sui/contracts/ccip_receiver/Move.toml @@ -0,0 +1,36 @@ +[package] +name = "ccip_receiver" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +ccip_receiver = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/chains/sui/contracts/ccip_receiver/sources/ccip_receiver.move b/chains/sui/contracts/ccip_receiver/sources/ccip_receiver.move new file mode 100644 index 0000000..bfd343b --- /dev/null +++ b/chains/sui/contracts/ccip_receiver/sources/ccip_receiver.move @@ -0,0 +1,21 @@ +module ccip_receiver::ccip_receiver; + +const EInvalidTimestamp: u64 = 1; + +public struct CCIP_RECEIVER has drop {} + +public struct CCIPReceiverState has key, store { + id: UID, +} + +fun init(_otw: CCIP_RECEIVER, ctx: &mut TxContext) { + let ccip_receiver_state = CCIPReceiverState { + id: object::new(ctx), + }; + + transfer::share_object(ccip_receiver_state); +} + +public fun ccip_receive() { + assert!(1 > 2, EInvalidTimestamp); +} diff --git a/chains/sui/contracts/ccip_receiver/tests/ccip_receiver_tests.move b/chains/sui/contracts/ccip_receiver/tests/ccip_receiver_tests.move new file mode 100644 index 0000000..843f239 --- /dev/null +++ b/chains/sui/contracts/ccip_receiver/tests/ccip_receiver_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module ccip_receiver::ccip_receiver_tests; +// uncomment this line to import the module +// use ccip_receiver::ccip_receiver; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_ccip_receiver() { + // pass +} + +#[test, expected_failure(abort_code = ::ccip_receiver::ccip_receiver_tests::ENotImplemented)] +fun test_ccip_receiver_fail() { + abort ENotImplemented +} +*/ diff --git a/chains/sui/contracts/ccip_router/.gitignore b/chains/sui/contracts/ccip_router/.gitignore new file mode 100644 index 0000000..813de75 --- /dev/null +++ b/chains/sui/contracts/ccip_router/.gitignore @@ -0,0 +1,4 @@ +build/* +traces/* +.trace +.coverage* diff --git a/chains/sui/contracts/ccip_router/Move.lock b/chains/sui/contracts/ccip_router/Move.lock new file mode 100644 index 0000000..d88f360 --- /dev/null +++ b/chains/sui/contracts/ccip_router/Move.lock @@ -0,0 +1,56 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 3 +manifest_digest = "07A725B2A701CF0EE14F47F75D20A3DD7E31CBCDD6609501137D0CC242E590AC" +deps_digest = "F9B494B64F0615AED0E98FC12A85B85ECD2BC5185C22D30E7F67786BB52E507C" +dependencies = [ + { id = "Bridge", name = "Bridge" }, + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "Bridge" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/bridge" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, + { id = "SuiSystem", name = "SuiSystem" }, +] + +[[move.package]] +id = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +id = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, +] + +[[move.package]] +id = "SuiSystem" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "c1f1ae650fb9f9248b39a569400b4420820868db", subdir = "crates/sui-framework/packages/sui-system" } + +dependencies = [ + { id = "MoveStdlib", name = "MoveStdlib" }, + { id = "Sui", name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.61.2" +edition = "2024.beta" +flavor = "sui" + +[env] + +[env.testnet] +chain-id = "4c78adac" +original-published-id = "0x0d1cefe6e43cc96cbb6b2e7aa261b8be05cbf926a8f83431e1bd6023250b522c" +latest-published-id = "0x0d1cefe6e43cc96cbb6b2e7aa261b8be05cbf926a8f83431e1bd6023250b522c" +published-version = "1" diff --git a/chains/sui/contracts/ccip_router/Move.toml b/chains/sui/contracts/ccip_router/Move.toml new file mode 100644 index 0000000..d8a17e8 --- /dev/null +++ b/chains/sui/contracts/ccip_router/Move.toml @@ -0,0 +1,36 @@ +[package] +name = "ccip_router" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +ccip_router = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/chains/sui/contracts/ccip_router/sources/ccip_router.move b/chains/sui/contracts/ccip_router/sources/ccip_router.move new file mode 100644 index 0000000..60a8bd8 --- /dev/null +++ b/chains/sui/contracts/ccip_router/sources/ccip_router.move @@ -0,0 +1,101 @@ +module ccip_router::router; + +use std::ascii; +use std::string::{Self, String}; +use std::type_name; +use sui::address; +use sui::derived_object; +use sui::event; +use sui::object; +use sui::transfer; +use sui::vec_map::{Self, VecMap}; + +public struct ROUTER has drop {} + +const EOnrampNotFound: u64 = 1; +const EParamsLengthMismatch: u64 = 2; +const EInvalidOnrampAddress: u64 = 3; + +public struct RouterObject has key { + id: UID, +} + +public struct OnRampSet has copy, drop { + dest_chain_selector: u64, + on_ramp_package_id: address, +} + +public struct RouterState has key { + id: UID, + on_ramp_package_ids: VecMap, // dest_chain_selector -> on_ramp_package_id +} + +public struct RouterStatePointer has key, store { + id: UID, + router_object_id: address, +} + +public fun type_and_version(): String { + string::utf8(b"Router 1.6.0") +} + +fun init(otw: ROUTER, ctx: &mut TxContext) { + let mut router_object = RouterObject { id: object::new(ctx) }; + + let router = RouterState { + id: derived_object::claim(&mut router_object.id, b"RouterState"), + on_ramp_package_ids: vec_map::empty(), + }; + + let router_state_pointer = RouterStatePointer { + id: object::new(ctx), + router_object_id: object::id_address(&router_object), + }; + + let tn = type_name::with_original_ids(); + let package_bytes = ascii::into_bytes(tn.address_string()); + let package_id = address::from_ascii_bytes(&package_bytes); + + transfer::share_object(router); + transfer::share_object(router_object); + + transfer::transfer(router_state_pointer, package_id); +} + +public fun is_chain_supported(router: &RouterState, dest_chain_selector: u64): bool { + router.on_ramp_package_ids.contains(&dest_chain_selector) +} + +// Returns the on ramp package id for the given destination chain selector. +public fun get_on_ramp(router: &RouterState, dest_chain_selector: u64): address { + assert!(router.on_ramp_package_ids.contains(&dest_chain_selector), EOnrampNotFound); + + *router.on_ramp_package_ids.get(&dest_chain_selector) +} + +public fun get_dest_chains(router: &RouterState): vector { + router.on_ramp_package_ids.keys() +} + +public fun set_on_ramps( + router: &mut RouterState, + dest_chain_selectors: vector, + on_ramp_package_ids: vector
, +) { + assert!(dest_chain_selectors.length() == on_ramp_package_ids.length(), EParamsLengthMismatch); + + let mut i = 0; + let selector_len = dest_chain_selectors.length(); + while (i < selector_len) { + let dest_chain_selector = dest_chain_selectors[i]; + let on_ramp_package_id = on_ramp_package_ids[i]; + assert!(on_ramp_package_id != @0x0, EInvalidOnrampAddress); + + if (router.on_ramp_package_ids.contains(&dest_chain_selector)) { + router.on_ramp_package_ids.remove(&dest_chain_selector); + }; + router.on_ramp_package_ids.insert(dest_chain_selector, on_ramp_package_id); + event::emit(OnRampSet { dest_chain_selector, on_ramp_package_id }); + i = i + 1; + }; +} diff --git a/chains/sui/contracts/ccip_router/tests/ccip_router_tests.move b/chains/sui/contracts/ccip_router/tests/ccip_router_tests.move new file mode 100644 index 0000000..b3e43cf --- /dev/null +++ b/chains/sui/contracts/ccip_router/tests/ccip_router_tests.move @@ -0,0 +1,18 @@ +/* +#[test_only] +module ccip_router::ccip_router_tests; +// uncomment this line to import the module +// use ccip_router::ccip_router; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_ccip_router() { + // pass +} + +#[test, expected_failure(abort_code = ::ccip_router::ccip_router_tests::ENotImplemented)] +fun test_ccip_router_fail() { + abort ENotImplemented +} +*/