-
Notifications
You must be signed in to change notification settings - Fork 11.2k
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
Accounts prototype (move side only) #20649
base: main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎ |
/// be created from a u64. This ensures that overflow is impossible unless | ||
/// 2^64 Sums are created and added together - a practical impossibility. | ||
public struct Sum has store { | ||
value: u128, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that this is overkill for supporting Balance. We could have another type called CappedSum { u64 }
for when values can't overflow.
But it would be dangerous to expose this type, since if it were improperly used by user code it could cause overflow.
So, we would have the option to special case Balance anyway and save 8 bytes if we want to, by having a public(package) CappedSum
type.
entry fun reserve<T>(owner: address, limit: u64, ctx: &TxContext): Reservation<T> { | ||
// TODO: handle sponsored transactions and (in the future) multi-agent transactions | ||
assert!(ctx.sender() == owner, ENotAccountOwner); | ||
return Reservation { owner, limit } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return Reservation { owner, limit } | |
Reservation { owner, limit } |
/// Because types must explicitly implement a conversion from their ordinary type to a mergable | ||
/// type (i.e. one made only of types defined in mergable.move), there is no need for an analogue | ||
/// to `public_transfer`. | ||
public fun transfer_to_account<T>(deposit: T, recipient: address) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was thinking about this, and I unfortunately don't think it's safe if:
recipient
is an object ID (rather than an end-user address)- We allow transfers to "wrapped" accounts (e.g.,
struct Obj has key { .., wrapped_account_id: ID }
)
The reason is that with wrapped Balance
/Coin
fields, you often want to maintain invariants that could be violated by a direct transfer. For example, in a classic CFMM pool
struct Pool has key {
// ...
coin1_account_id: ID, // before: Balance<T1>
coin2_account_id: ID, // before: Balance<T2>
}
, you would want to maintain the invariant balance(coin1_account_id) = k * balance(coin2_account_id)
. With the current Balance
-based approach, you leverage encapsulation to enforce this--e.g. only allow writes in swap
, and only if the invariant are maintained. But with an implementation that used wrapped accounts, it would not be possible to enforce this invariant because of (2) (e.g., can just deposit directly to balance 1 without touching balance 2).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two approaches come to mind for addressing this--the key ideas behind both are to enforce encapsulation on transfers to "object accounts":
A. transfer_to_account
requires a &mut UID
or &ID
of object O
to transfer to an object account for O
(not needed for an address account, of course)
B. Object accounts must be explicitly created, and have a different/more structured API for transfers than addresses. E.g.,
struct Account has store {
id: UID,
}
public fun create(ctx: &mut TxContext): Account
// could also split into more fine-grained caps
public fun transfer_to_account<T>(deposit: T, account: &mut Account)
Prototype of the move-side implementation of sui accounts.
The basic design is this:
Balance<T>
) to a type that supports merging (e.g.MergableBalance<T>
)mergable.move
mergable.rs
Things to note:
Important TODOs:
transfer_to_account
private in the same way thattransfer
isreserve
is only called with an Input, not a result. I'm very open to better ideas of how to do reservations.Things to not worry about: