Skip to content

Commit b159ce7

Browse files
committed
MCMS support LockReleaseTokenPool RebalancerCap
1 parent 4581931 commit b159ce7

File tree

6 files changed

+963
-17
lines changed

6 files changed

+963
-17
lines changed

bindings/generated/ccip/ccip_token_pools/lock_release_token_pool/lock_release_token_pool.go

Lines changed: 239 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/generated/mcms/mcms_registry/mcms_registry.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move

Lines changed: 176 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::type_name::{Self, TypeName};
1515
use sui::address;
1616
use sui::clock::Clock;
1717
use sui::coin::{Self, Coin, CoinMetadata, TreasuryCap};
18+
use sui::event;
1819
use sui::package::UpgradeCap;
1920

2021
#[allow(lint(coin_field))]
@@ -30,13 +31,23 @@ public struct RebalancerCap has key, store {
3031
id: UID,
3132
}
3233

34+
public struct RebalancerSet has copy, drop {
35+
old_rebalancer_cap_id: ID,
36+
new_rebalancer_cap_id: ID,
37+
}
38+
3339
const CLOCK_ADDRESS: address = @0x6;
3440

3541
const EInvalidArguments: u64 = 1;
3642
const ETokenPoolBalanceTooLow: u64 = 2;
3743
const EInvalidOwnerCap: u64 = 3;
3844
const EInvalidFunction: u64 = 4;
3945
const EInvalidRebalancerCap: u64 = 5;
46+
const EMcmsRebalancerAlreadySet: u64 = 6;
47+
const ERebalancerCapIsInUse: u64 = 7;
48+
const ERebalancerCapNotTransferredOut: u64 = 8;
49+
const EInvalidRebalancer: u64 = 9;
50+
const ERebalancerCapDoesNotExist: u64 = 10;
4051

4152
// ================================================================
4253
// | Init |
@@ -515,6 +526,145 @@ public fun get_rebalancer<T>(state: &LockReleaseTokenPoolState<T>): address {
515526
object::id_to_address(&state.rebalancer_cap_id)
516527
}
517528

529+
public struct McmsCap has key, store {
530+
id: UID,
531+
owner_cap: OwnerCap,
532+
rebalancer_cap: Option<RebalancerCap>,
533+
}
534+
535+
fun new_mcms_cap(
536+
owner_cap: OwnerCap,
537+
rebalancer_cap: Option<RebalancerCap>,
538+
ctx: &mut TxContext,
539+
): McmsCap {
540+
McmsCap {
541+
id: object::new(ctx),
542+
owner_cap,
543+
rebalancer_cap,
544+
}
545+
}
546+
547+
public fun mcms_set_rebalancer<T>(
548+
state: &mut LockReleaseTokenPoolState<T>,
549+
registry: &mut Registry,
550+
params: ExecutingCallbackParams,
551+
ctx: &mut TxContext,
552+
) {
553+
let (mcms_cap, function, data) = mcms_registry::get_callback_params_with_caps<
554+
McmsCallback<T>,
555+
McmsCap,
556+
>(
557+
registry,
558+
McmsCallback<T> {},
559+
params,
560+
);
561+
assert!(function == string::utf8(b"set_rebalancer"), EInvalidFunction);
562+
563+
let mut stream = bcs_stream::new(data);
564+
bcs_stream::validate_obj_addrs(
565+
vector[object::id_address(state), object::id_address(&mcms_cap.owner_cap)],
566+
&mut stream,
567+
);
568+
let rebalancer = bcs_stream::deserialize_address(&mut stream);
569+
bcs_stream::assert_is_consumed(&stream);
570+
571+
if (rebalancer == mcms_registry::get_multisig_address()) {
572+
assert!(mcms_cap.rebalancer_cap.is_none(), EMcmsRebalancerAlreadySet);
573+
574+
let new_rebalancer_cap = RebalancerCap {
575+
id: object::new(ctx),
576+
};
577+
let new_rebalancer_cap_id = object::id(&new_rebalancer_cap);
578+
mcms_cap.rebalancer_cap.fill(new_rebalancer_cap);
579+
580+
set_rebalancer_cap_id(state, &mcms_cap.owner_cap, new_rebalancer_cap_id, ctx);
581+
} else {
582+
set_rebalancer(state, &mcms_cap.owner_cap, rebalancer, ctx);
583+
// If MCMS previously owned the rebalancer cap, we destroy it now that MCMS no longer owns it
584+
if (mcms_cap.rebalancer_cap.is_some()) {
585+
destroy_rebalancer_cap(state, mcms_cap.rebalancer_cap.extract(), ctx);
586+
}
587+
}
588+
}
589+
590+
public fun set_rebalancer<T>(
591+
state: &mut LockReleaseTokenPoolState<T>,
592+
owner_cap: &OwnerCap,
593+
rebalancer: address,
594+
ctx: &mut TxContext,
595+
) {
596+
assert!(rebalancer != mcms_registry::get_multisig_address(), EInvalidRebalancer);
597+
598+
let rebalancer_cap = RebalancerCap {
599+
id: object::new(ctx),
600+
};
601+
602+
// Update `LockReleaseTokenPoolState` before sending to rebalancer address
603+
set_rebalancer_cap_id(
604+
state,
605+
owner_cap,
606+
object::id(&rebalancer_cap),
607+
ctx,
608+
);
609+
610+
transfer::public_transfer(rebalancer_cap, rebalancer);
611+
}
612+
613+
/// Only owner can set rebalancer cap id
614+
fun set_rebalancer_cap_id<T>(
615+
state: &mut LockReleaseTokenPoolState<T>,
616+
owner_cap: &OwnerCap,
617+
new_rebalancer_cap_id: ID,
618+
_ctx: &mut TxContext,
619+
) {
620+
assert!(object::id(owner_cap) == ownable::owner_cap_id(&state.ownable_state), EInvalidOwnerCap);
621+
622+
let old_rebalancer_cap_id = state.rebalancer_cap_id;
623+
state.rebalancer_cap_id = new_rebalancer_cap_id;
624+
625+
event::emit(RebalancerSet { old_rebalancer_cap_id, new_rebalancer_cap_id });
626+
}
627+
628+
public fun mcms_destroy_mcms_cap<T>(
629+
state: &mut LockReleaseTokenPoolState<T>,
630+
registry: &mut Registry,
631+
params: ExecutingCallbackParams,
632+
ctx: &mut TxContext,
633+
) {
634+
let (mcms_cap, function, data) = mcms_registry::get_callback_params_with_caps<
635+
McmsCallback<T>,
636+
McmsCap,
637+
>(
638+
registry,
639+
McmsCallback<T> {},
640+
params,
641+
);
642+
assert!(function == string::utf8(b"destroy_mcms_cap"), EInvalidFunction);
643+
assert!(mcms_cap.rebalancer_cap.is_some(), ERebalancerCapDoesNotExist);
644+
645+
let mut stream = bcs_stream::new(data);
646+
bcs_stream::validate_obj_addrs(
647+
vector[object::id_address(state), object::id_address(mcms_cap.rebalancer_cap.borrow())],
648+
&mut stream,
649+
);
650+
bcs_stream::assert_is_consumed(&stream);
651+
652+
let rebalancer_cap = mcms_cap.rebalancer_cap.extract();
653+
destroy_rebalancer_cap(state, rebalancer_cap, ctx);
654+
}
655+
656+
/// Clean up old rebalancer caps not in use, anyone can call this function to clean up old rebalancer caps not in use.
657+
public fun destroy_rebalancer_cap<T>(
658+
state: &mut LockReleaseTokenPoolState<T>,
659+
rebalancer_cap: RebalancerCap,
660+
_ctx: &mut TxContext,
661+
) {
662+
assert!(state.rebalancer_cap_id != object::id(&rebalancer_cap), ERebalancerCapIsInUse);
663+
664+
let RebalancerCap { id } = rebalancer_cap;
665+
object::delete(id);
666+
}
667+
518668
#[test_only]
519669
public fun create_fake_rebalancer_cap(ctx: &mut TxContext): RebalancerCap {
520670
RebalancerCap {
@@ -597,17 +747,20 @@ public fun execute_ownership_transfer<T>(
597747
ownable::execute_ownership_transfer(owner_cap, &mut state.ownable_state, to, ctx);
598748
}
599749

750+
/// Setting rebalancer cap should be called via `mcms_set_rebalancer`
600751
public fun execute_ownership_transfer_to_mcms<T>(
601752
owner_cap: OwnerCap,
602753
state: &mut LockReleaseTokenPoolState<T>,
603754
registry: &mut Registry,
604755
to: address,
605756
ctx: &mut TxContext,
606757
) {
607-
ownable::execute_ownership_transfer_to_mcms(
608-
owner_cap,
758+
assert!(object::id(&owner_cap) == state.ownable_state.owner_cap_id(), EInvalidOwnerCap);
759+
760+
ownable::execute_ownership_and_cap_transfer_to_mcms(
609761
&mut state.ownable_state,
610762
registry,
763+
new_mcms_cap(owner_cap, option::none(), ctx),
611764
to,
612765
McmsCallback<T> {},
613766
vector[b"lock_release_token_pool"],
@@ -946,9 +1099,9 @@ public fun mcms_transfer_ownership<T>(
9461099
params: ExecutingCallbackParams,
9471100
ctx: &mut TxContext,
9481101
) {
949-
let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps<
1102+
let (mcms_cap, function, data) = mcms_registry::get_callback_params_with_caps<
9501103
McmsCallback<T>,
951-
OwnerCap,
1104+
McmsCap,
9521105
>(
9531106
registry,
9541107
McmsCallback<T> {},
@@ -958,14 +1111,14 @@ public fun mcms_transfer_ownership<T>(
9581111

9591112
let mut stream = bcs_stream::new(data);
9601113
bcs_stream::validate_obj_addrs(
961-
vector[object::id_address(state), object::id_address(owner_cap)],
1114+
vector[object::id_address(state), object::id_address(&mcms_cap.owner_cap)],
9621115
&mut stream,
9631116
);
9641117

9651118
let to = bcs_stream::deserialize_address(&mut stream);
9661119
bcs_stream::assert_is_consumed(&stream);
9671120

968-
transfer_ownership(state, owner_cap, to, ctx);
1121+
transfer_ownership(state, &mcms_cap.owner_cap, to, ctx);
9691122
}
9701123

9711124
public fun mcms_execute_ownership_transfer<T>(
@@ -974,9 +1127,9 @@ public fun mcms_execute_ownership_transfer<T>(
9741127
params: ExecutingCallbackParams,
9751128
ctx: &mut TxContext,
9761129
) {
977-
let (_owner_cap, function, data) = mcms_registry::get_callback_params_with_caps<
1130+
let (mcms_cap, function, data) = mcms_registry::get_callback_params_with_caps<
9781131
McmsCallback<T>,
979-
OwnerCap,
1132+
McmsCap,
9801133
>(
9811134
registry,
9821135
McmsCallback<T> {},
@@ -986,14 +1139,22 @@ public fun mcms_execute_ownership_transfer<T>(
9861139

9871140
let mut stream = bcs_stream::new(data);
9881141
bcs_stream::validate_obj_addrs(
989-
vector[object::id_address(_owner_cap), object::id_address(state)],
1142+
vector[object::id_address(&mcms_cap.owner_cap), object::id_address(state)],
9901143
&mut stream,
9911144
);
9921145

9931146
let to = bcs_stream::deserialize_address(&mut stream);
9941147
bcs_stream::assert_is_consumed(&stream);
9951148

996-
let owner_cap = mcms_registry::release_cap(registry, McmsCallback<T> {});
1149+
let McmsCap { id, owner_cap, rebalancer_cap } = mcms_registry::release_cap(
1150+
registry,
1151+
McmsCallback<T> {},
1152+
);
1153+
assert!(rebalancer_cap.is_none(), ERebalancerCapNotTransferredOut);
1154+
1155+
rebalancer_cap.destroy_none();
1156+
object::delete(id);
1157+
9971158
execute_ownership_transfer(owner_cap, state, to, ctx);
9981159
}
9991160

@@ -1078,3 +1239,8 @@ public fun destroy_token_pool<T>(
10781239

10791240
reserve
10801241
}
1242+
1243+
#[test_only]
1244+
public fun mcms_rebalancer_cap_address(mcms_cap: &McmsCap): address {
1245+
object::id_address(mcms_cap.rebalancer_cap.borrow())
1246+
}

contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/ownable.move

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,35 @@ public fun execute_ownership_transfer_to_mcms<T: drop>(
223223
event::emit(OwnershipTransferred { from: current_owner, to: new_owner });
224224
}
225225

226+
/// Used for registering a custom Cap type with MCMS, e.g. McmsCap for LockReleaseTokenPool.
227+
/// Needed as `OwnableState` does not know about the custom Cap type and needs updating.
228+
/// Calling function should assert the owner cap inside `cap` matches `ownable_state.owner_cap_id`.
229+
public(package) fun execute_ownership_and_cap_transfer_to_mcms<C: key + store, T: drop>(
230+
state: &mut OwnableState,
231+
registry: &mut Registry,
232+
cap: C,
233+
to: address,
234+
proof: T,
235+
allowed_modules: vector<vector<u8>>,
236+
ctx: &mut TxContext,
237+
) {
238+
assert!(state.pending_transfer.is_some(), ENoPendingTransfer);
239+
240+
let pending_transfer = state.pending_transfer.extract();
241+
let current_owner = state.owner;
242+
243+
assert!(pending_transfer.from == current_owner, EOwnerChanged);
244+
assert!(pending_transfer.to == to, EProposedOwnerMismatch);
245+
assert!(pending_transfer.accepted, ETransferNotAccepted);
246+
assert!(to == mcms_registry::get_multisig_address(), EMustTransferToMcms);
247+
248+
state.owner = to;
249+
state.pending_transfer = option::none();
250+
251+
mcms_registry::register_entrypoint(registry, proof, cap, allowed_modules, ctx);
252+
event::emit(OwnershipTransferred { from: current_owner, to });
253+
}
254+
226255
public fun destroy(state: OwnableState, owner_cap: OwnerCap, _ctx: &mut TxContext) {
227256
let OwnableState {
228257
id: state_id,

0 commit comments

Comments
 (0)