Skip to content

Conversation

sblackshear
Copy link
Collaborator

Add functions to determine whether whether a coin is regulated + whether it supports global pause or not by inspecting the metadata. Handy for use-cases that want to special case these coins.

Copy link

vercel bot commented Oct 14, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sui-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 22, 2024 9:28pm
3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Oct 22, 2024 9:28pm
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Oct 22, 2024 9:28pm
sui-typescript-docs ⬜️ Ignored (Inspect) Visit Preview Oct 22, 2024 9:28pm

/// Return None if T was created at an epoch before `REGULATED_MARKER_START_EPOCH`. Before
/// this epoch, it was not possible to distinguish between regulated and unregulated currencies by
/// looking at the metadata.
public fun is_regulated_currency<T>(metadata: &CoinMetadata<T>, ctx: &mut TxContext): Option<bool> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think context should be mutable here. Can't think of a single scenario where we'd find it useful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point + I'm surprised there's no linter or compiler warning to yell at me. Is this because it would be too noisy in the case that folks do this for upgrade friendliness, and it's too hard to distinguish between that case and this one?

});
if (ctx.epoch() >= REGULATED_MARKER_START_EPOCH) {
dynamic_field::add(&mut metadata.id, RegulatedMarker {}, allow_global_pause);
assert!(is_regulated_currency(&metadata, ctx).extract());
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason behind this assert? May be missing something, but we make this assertion true in the line above, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Leftover debugging code; will remove

coin_metadata_object: object::id(&metadata),
deny_cap_object: object::id(&deny_cap),
});
if (ctx.epoch() >= REGULATED_MARKER_START_EPOCH) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to do epoch checks on creation / migration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's a fair question--I guess there's no harm to adding the marker right away. I will do that instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Reworked a lot of stuff based on this--now using an enum that gets added to every metadata (not jus the regulated one), and no more epoch checks. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

Love that way more, feels cleaner too!

Generally, this looks good to me. If we're definitely aligned on having this on the framework and not waiting for metadata V2!

false,
ctx,
);
assert!(coin::is_regulated_currency(&metadata).is_some());
Copy link
Contributor

Choose a reason for hiding this comment

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

Blind (untested) comment but receiver syntax should work, no? They are both in the coin module (can be applied to all tests / calls).

Suggested change
assert!(coin::is_regulated_currency(&metadata).is_some());
assert!(metadata.is_regulated_currency().is_some());

@manolisliolios
Copy link
Contributor

@sblackshear should we also allow existing regulated coins to migrate their metadata (add the marker) by presenting the DenyCapV2?

@sblackshear
Copy link
Collaborator Author

sblackshear commented Oct 17, 2024

@sblackshear should we also allow existing regulated coins to migrate their metadata (add the marker) by presenting the DenyCapV2?

I looked into this, but I don't think it's easy to do in a foolproof way because of the supported DenyCap -> DenyCapV2 migration. E.g., someone might add the marker with DenyCap and get allows_global_pause, then migrate DenyCap to DenyCapV2 and switch on the global pause without updating the metadata. So I'd rather leave this out and add it if someone needs it, with the semantics they'd expect.


/// A dynamic field key added to `CoinMetadata` objects created after protocol version 63.
/// This is bound to a `RegulatedInfo` enum
public struct RegulatedMarker has copy, drop, store {}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
public struct RegulatedMarker has copy, drop, store {}
public struct RegulatedMarker() has copy, drop, store;

// b"Kill switch was not allowed at the creation of the DenyCapV2";
const EGlobalPauseNotAllowed: u64 = 3;


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change


/// Return Some(true) if `T` is a regulated currency that allows global pause, Some(false) if it is
/// regulated but does not allow global pause, or is an ordinary currency.
/// Return None if T was created at an epoch before `REGULATED_MARKER_START_EPOCH`. Before
Copy link
Contributor

Choose a reason for hiding this comment

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

What is REGULATED_MARKER_START_EPOCH?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call--this is tale + I will fix

/// An ordinary coin that does not support a denylist or global pause
Ordinary,
/// A regulated coin that supports a denylist, and (maybe) global paus
Regulated { allow_global_pause: bool}
Copy link
Contributor

Choose a reason for hiding this comment

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

What if we did something like

Suggested change
Regulated { allow_global_pause: bool}
Regulated { deny_cap: ID }

which points to the DenyCapV2. Obviously some issues if the object is wrapped/deleted, but the flag allow_global_pause cannot be modified after creation, so it is fine to look at the last time the DenyCap existed.

My goal here is to allow existing coins (at least regulated ones) to add their RegulatedInfo after the fact.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The intention of this change is to allow Move code to special-case regulated coins (e.g., DEX pool creation, which typically already asks for CoinMetadata to get the decimals). If the Move code wants to treat global pause coins differently, it needs a boolean, not a pointer.

Would be fine adding the deny cap ID as additional metadata--more info seems better for sure

coin_metadata_object: object::id(&metadata),
deny_cap_object: object::id(&deny_cap),
});
dynamic_field::add(
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we're allowing both V1 and V2 denycaps, do we really need to store the id of the deny_cap in the marker? It can get invalid the moment you migrate V1 to V2.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that's fair, the migration is giant PiTA that basically makes everything potentially inconsistent except for marking the metadata as regulated/not. Will revert to that

dynamic_field::add(
&mut metadata.id,
RegulatedMarker(),
RegulatedInfo::Regulated { allow_global_pause: false, deny_cap_id: object::id(&deny_cap) }
Copy link
Contributor

@manolisliolios manolisliolios Oct 21, 2024

Choose a reason for hiding this comment

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

allow_global_pause can end up converted to true when migrating to v2, while the metadata wouldn't reflect. Isn't this a small vulnerability for protocols who might trust this field?

Add functions to determine whether whether a coin is regulated + whether it supports global pause or not by inspecting the metadata. Handy for use-cases that want to special case these coins.
Copy link
Contributor

This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale label Dec 22, 2024
Copy link
Contributor

@manolisliolios manolisliolios left a comment

Choose a reason for hiding this comment

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

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants