Skip to content

Commit 3068ae8

Browse files
authored
Add DeepBook core account wrapper (#1091)
* feat: add deepbook core account wrapper * Fix Move CI test environment * Simplify DeepBook core account wrapper surface * Split DeepBook core account data module * Emit DeepBook core account init event * Restore Move test workflow command * Fund DeepBook account wrapper from full balances * No-op uninitialized DeepBook account actions * Clarify DeepBook account getter names
1 parent ed1c054 commit 3068ae8

5 files changed

Lines changed: 1124 additions & 0 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Generated by move; do not edit
2+
# This file should be checked in.
3+
4+
[move]
5+
version = 4
6+
7+
[pinned.testnet.MoveStdlib]
8+
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "1fd64849c11a130de450168e64e3913885a3bee4" }
9+
use_environment = "testnet"
10+
manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363"
11+
deps = {}
12+
13+
[pinned.testnet.MoveStdlib_1]
14+
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "367fd808279bed26f7c64fc63160062a2ee29ab7" }
15+
use_environment = "testnet"
16+
manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363"
17+
deps = {}
18+
19+
[pinned.testnet.Sui]
20+
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "1fd64849c11a130de450168e64e3913885a3bee4" }
21+
use_environment = "testnet"
22+
manifest_digest = "7AFB66695545775FBFBB2D3078ADFD084244D5002392E837FDE21D9EA1C6D01C"
23+
deps = { MoveStdlib = "MoveStdlib" }
24+
25+
[pinned.testnet.Sui_1]
26+
source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "367fd808279bed26f7c64fc63160062a2ee29ab7" }
27+
use_environment = "testnet"
28+
manifest_digest = "7AFB66695545775FBFBB2D3078ADFD084244D5002392E837FDE21D9EA1C6D01C"
29+
deps = { MoveStdlib = "MoveStdlib_1" }
30+
31+
[pinned.testnet.account]
32+
source = { local = "../account" }
33+
use_environment = "testnet"
34+
manifest_digest = "5745706258F61D6CE210904B3E6AE87A73CE9D31A6F93BE4718C442529332A87"
35+
deps = { std = "MoveStdlib_1", sui = "Sui_1" }
36+
37+
[pinned.testnet.deepbook]
38+
source = { local = "../deepbook" }
39+
use_environment = "testnet"
40+
manifest_digest = "3101923B9428545A4F52FFAD1C4F959F9BFFF84CD09CE4BCC1CB831286999B5A"
41+
deps = { std = "MoveStdlib_1", sui = "Sui_1", token = "token" }
42+
43+
[pinned.testnet.deepbook_core_account]
44+
source = { root = true }
45+
use_environment = "testnet"
46+
manifest_digest = "329DA1355FB6149935CF8199805E2A3C7A21AB88A845218BE430361503374959"
47+
deps = { MoveStdlib = "MoveStdlib", Sui = "Sui", account = "account", deepbook = "deepbook", token = "token" }
48+
49+
[pinned.testnet.token]
50+
source = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "0a2b9f3e83d8a25d30df4e250768fbbc678abcb6" }
51+
use_environment = "testnet"
52+
manifest_digest = "5745706258F61D6CE210904B3E6AE87A73CE9D31A6F93BE4718C442529332A87"
53+
deps = { std = "MoveStdlib_1", sui = "Sui_1" }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "deepbook_core_account"
3+
edition = "2024.beta"
4+
version = "0.0.1"
5+
6+
[dependencies]
7+
account = { local = "../account" }
8+
deepbook = { local = "../deepbook" }
9+
token = { git = "https://github.com/MystenLabs/deepbookv3.git", subdir = "packages/token", rev = "main" }
10+
MoveStdlib = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "1fd64849c11a130de450168e64e3913885a3bee4", override = true }
11+
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "1fd64849c11a130de450168e64e3913885a3bee4", override = true }
12+
13+
[addresses]
14+
deepbook_core_account = "0x0"
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright (c) Mysten Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/// Account app-data for the DeepBook core wrapper. This module owns the embedded
5+
/// balance manager slot and exposes only package-scoped helpers for the public
6+
/// facade to fund, trade, and sweep through that manager.
7+
module deepbook_core_account::account_data;
8+
9+
use account::{
10+
account::{Account, Auth},
11+
account_registry::{Self as account_registry, AccountRegistry}
12+
};
13+
use deepbook::{
14+
balance_manager::{Self, BalanceManager, DepositCap, TradeCap, TradeProof, WithdrawCap},
15+
registry::Registry
16+
};
17+
use std::internal::permit;
18+
use sui::{accumulator::AccumulatorRoot, clock::Clock, coin::{Self, Coin}, event};
19+
use token::deep::DEEP;
20+
21+
/// App witness that namespaces DeepBook core account data on an `Account`.
22+
public struct DeepbookCoreAccountApp has drop {}
23+
24+
/// Per-account DeepBook core state. The embedded manager ID is stable across all
25+
/// wrapped calls, so resting orders remain addressable by DeepBook core state.
26+
public struct DeepbookCoreAccountData has store {
27+
balance_manager: BalanceManager,
28+
deposit_cap: DepositCap,
29+
withdraw_cap: WithdrawCap,
30+
trade_cap: TradeCap,
31+
}
32+
33+
/// Emitted once when a canonical account first gets DeepBook core account data.
34+
public struct DeepbookCoreAccountInitialized has copy, drop {
35+
account_id: ID,
36+
account_owner: address,
37+
wrapper_id: ID,
38+
balance_manager_id: ID,
39+
}
40+
41+
/// Return whether this account already has a DeepBook core account slot.
42+
public fun is_initialized(account: &Account): bool {
43+
account.has_data<DeepbookCoreAccountApp>()
44+
}
45+
46+
/// Return this account's embedded balance-manager ID, if the slot exists.
47+
public fun balance_manager_id(account: &Account): Option<ID> {
48+
if (!is_initialized(account)) {
49+
option::none()
50+
} else {
51+
option::some(borrow(account).balance_manager.id())
52+
}
53+
}
54+
55+
/// Return the free balance of `T` currently sitting in the embedded manager.
56+
public fun balance_manager_balance<T>(account: &Account): u64 {
57+
if (!is_initialized(account)) {
58+
0
59+
} else {
60+
borrow(account).balance_manager.balance<T>()
61+
}
62+
}
63+
64+
public(package) fun generate_auth_as_app(account_registry: &AccountRegistry): Auth {
65+
account_registry::generate_auth_as_app<DeepbookCoreAccountApp>(
66+
account_registry,
67+
permit<DeepbookCoreAccountApp>(),
68+
)
69+
}
70+
71+
public(package) fun borrow(account: &Account): &DeepbookCoreAccountData {
72+
account.borrow_data<DeepbookCoreAccountApp, DeepbookCoreAccountData>()
73+
}
74+
75+
public(package) fun ensure(
76+
account: &mut Account,
77+
deepbook_registry: &Registry,
78+
ctx: &mut TxContext,
79+
) {
80+
if (!is_initialized(account)) {
81+
let (
82+
balance_manager,
83+
deposit_cap,
84+
withdraw_cap,
85+
trade_cap,
86+
) = balance_manager::new_with_custom_owner_caps_v2<DeepbookCoreAccountApp>(
87+
DeepbookCoreAccountApp {},
88+
deepbook_registry,
89+
account.account_id().to_address(),
90+
ctx,
91+
);
92+
let account_id = account.account_id();
93+
let account_owner = account.owner();
94+
let wrapper_id = account.receive_address().to_id();
95+
let balance_manager_id = balance_manager.id();
96+
account.attach(
97+
permit<DeepbookCoreAccountApp>(),
98+
DeepbookCoreAccountData { balance_manager, deposit_cap, withdraw_cap, trade_cap },
99+
);
100+
event::emit(DeepbookCoreAccountInitialized {
101+
account_id,
102+
account_owner,
103+
wrapper_id,
104+
balance_manager_id,
105+
});
106+
};
107+
}
108+
109+
public(package) fun borrow_mut(account: &mut Account): &mut DeepbookCoreAccountData {
110+
account.borrow_data_mut<DeepbookCoreAccountApp, DeepbookCoreAccountData>(
111+
permit<DeepbookCoreAccountApp>(),
112+
)
113+
}
114+
115+
public(package) fun balance_manager(d: &DeepbookCoreAccountData): &BalanceManager {
116+
&d.balance_manager
117+
}
118+
119+
public(package) fun balance_manager_mut(d: &mut DeepbookCoreAccountData): &mut BalanceManager {
120+
&mut d.balance_manager
121+
}
122+
123+
public(package) fun generate_trader_proof(
124+
d: &mut DeepbookCoreAccountData,
125+
ctx: &TxContext,
126+
): TradeProof {
127+
d.balance_manager.generate_proof_as_trader(&d.trade_cap, ctx)
128+
}
129+
130+
public(package) fun sweep_all<BaseAsset, QuoteAsset>(
131+
d: &mut DeepbookCoreAccountData,
132+
ctx: &mut TxContext,
133+
): (Coin<BaseAsset>, Coin<QuoteAsset>, Coin<DEEP>) {
134+
(
135+
sweep_from_manager<BaseAsset>(d, ctx),
136+
sweep_from_manager<QuoteAsset>(d, ctx),
137+
sweep_from_manager<DEEP>(d, ctx),
138+
)
139+
}
140+
141+
public(package) fun deposit_all<BaseAsset, QuoteAsset>(
142+
account: &mut Account,
143+
base: Coin<BaseAsset>,
144+
quote: Coin<QuoteAsset>,
145+
deep: Coin<DEEP>,
146+
) {
147+
deposit_to_account_if_nonzero<BaseAsset>(account, base);
148+
deposit_to_account_if_nonzero<QuoteAsset>(account, quote);
149+
deposit_to_account_if_nonzero<DEEP>(account, deep);
150+
}
151+
152+
public(package) fun withdraw_all<BaseAsset, QuoteAsset>(
153+
account: &mut Account,
154+
root: &AccumulatorRoot,
155+
clock: &Clock,
156+
ctx: &mut TxContext,
157+
): (Coin<BaseAsset>, Coin<QuoteAsset>, Coin<DEEP>) {
158+
(
159+
withdraw_all_of_type<BaseAsset>(account, root, clock, ctx),
160+
withdraw_all_of_type<QuoteAsset>(account, root, clock, ctx),
161+
withdraw_all_of_type<DEEP>(account, root, clock, ctx),
162+
)
163+
}
164+
165+
fun withdraw_all_of_type<T>(
166+
account: &mut Account,
167+
root: &AccumulatorRoot,
168+
clock: &Clock,
169+
ctx: &mut TxContext,
170+
): Coin<T> {
171+
let amount = account.balance<T>(root, clock);
172+
if (amount == 0) {
173+
coin::zero<T>(ctx)
174+
} else {
175+
account.withdraw<T>(amount, ctx)
176+
}
177+
}
178+
179+
public(package) fun deposit_to_manager_if_nonzero<T>(
180+
d: &mut DeepbookCoreAccountData,
181+
coin: Coin<T>,
182+
ctx: &TxContext,
183+
) {
184+
if (coin.value() == 0) {
185+
coin.destroy_zero();
186+
} else {
187+
d.balance_manager.deposit_with_cap<T>(&d.deposit_cap, coin, ctx);
188+
}
189+
}
190+
191+
fun sweep_from_manager<T>(d: &mut DeepbookCoreAccountData, ctx: &mut TxContext): Coin<T> {
192+
let amount = d.balance_manager.balance<T>();
193+
if (amount == 0) {
194+
coin::zero<T>(ctx)
195+
} else {
196+
d.balance_manager.withdraw_with_cap<T>(&d.withdraw_cap, amount, ctx)
197+
}
198+
}
199+
200+
fun deposit_to_account_if_nonzero<T>(account: &mut Account, coin: Coin<T>) {
201+
if (coin.value() == 0) {
202+
coin.destroy_zero();
203+
} else {
204+
account.deposit<T>(coin);
205+
}
206+
}

0 commit comments

Comments
 (0)