Skip to content

Commit 12fa433

Browse files
authored
add optional term extension feature (#13)
* add optional term extension feature * update test script
1 parent e12d334 commit 12fa433

File tree

4 files changed

+645
-48
lines changed

4 files changed

+645
-48
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ The deposit pool module allows users to deposit base tokens and earn loyalty tok
4242
- Pending withdrawal period (optional)
4343
- Admin-controlled reward pool integration
4444
- Receipts for each deposit, enabling precise reward calculation
45+
- Admin-controlled term extending option
46+
- User can extend a pre-mature existed deposit to a higher term
4547

4648
#### Usage
4749

deploy.sh

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,14 @@ echo "Talus coin at : \"$TALUS_COIN\""
130130
###########################################
131131
# Faucet Deployment (Optional)
132132
###########################################
133+
# Calculate split amounts for faucet
134+
if [ -z "$INIT_AMOUNT" ]; then
135+
SPLIT_AMOUNT=$DEFAULT_INIT
136+
else
137+
SPLIT_AMOUNT=$(_calculate_amount "$TOTAL_SUPPLY-$INIT_AMOUNT")
138+
fi
133139

134140
if [[ "${DEPLOY_FAUCET,,}" =~ ^(y|yes)$ ]]; then
135-
# Calculate split amounts for faucet
136-
if [ -z "$INIT_AMOUNT" ]; then
137-
SPLIT_AMOUNT=$DEFAULT_INIT
138-
else
139-
SPLIT_AMOUNT=$(_calculate_amount "$TOTAL_SUPPLY-$INIT_AMOUNT")
140-
fi
141141

142142
echo "Split coin"
143143
_spliter=$($SUI client split-coin --coin-id $TALUS_COIN --amounts $SPLIT_AMOUNT)
@@ -184,8 +184,8 @@ echo "Loyalty Program Contract at: \"$LoyaltyProgramContractID\""
184184
echo "Loyalty Token Contract at: \"$LoyaltyTokenContractID\""
185185
echo "Loyalty Token Cap at: \"$LoyaltyTreasuryCap\""
186186

187-
# Initialize reward pool
188-
echo "Init reward pool and Deposit Pool"
187+
# Initialize deposit pool
188+
echo "Init Deposit pool"
189189
script=$($SUI client call --package $LoyaltyProgramContractID --module deposit_pool --function new \
190190
--type-args $TOKEN_CONTRACT_ID::us::US --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
191191
--args $LoyaltyTreasuryCap --args $BASE_APY --args [] --args false --args 0 \
@@ -239,7 +239,7 @@ echo "Policy at $PolicyID"
239239
# --type-args $TOKEN_CONTRACT_ID::us::US \
240240
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
241241
# --args $DEPOSIT_POOL \
242-
# --args <us coin> \
242+
# --args <us coin id> \
243243
# --args 0 --args $USER \
244244
# --args [] --args 0x6 --dry-run
245245

@@ -250,8 +250,53 @@ echo "Policy at $PolicyID"
250250
# --args 0x6 --gas-budget 10000000
251251

252252
# echo "Test claim reward"
253-
# $SUI client call --package $LoyaltyProgramContractID --module reward_program \
253+
# $SUI client call --package $LoyaltyProgramContractID --module reward_pool \
254254
# --function claim \
255255
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
256256
# --type-args $TOKEN_CONTRACT_ID::us::US
257257
# --args $DEPOSIT_POOL --args <token id> --args $PolicyID --args [] \
258+
259+
# echo "Test add term"
260+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
261+
# --function upsert_lock_term \
262+
# --type-args $TOKEN_CONTRACT_ID::us::US \
263+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
264+
# --args $DEPOSIT_POOL --args $ADMIN_CAP --args 30 --args 10 --dry-run
265+
266+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
267+
# --function upsert_lock_term \
268+
# --type-args $TOKEN_CONTRACT_ID::us::US \
269+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
270+
# --args $DEPOSIT_POOL --args $ADMIN_CAP --args 60 --args 20 --dry-run
271+
272+
273+
# echo "Test deposit to pool"
274+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
275+
# --function deposit \
276+
# --type-args $TOKEN_CONTRACT_ID::us::US \
277+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
278+
# --args $DEPOSIT_POOL \
279+
# --args <us coin id> \
280+
# --args 30 --args $USER \
281+
# --args [] --args 0x6 --dry-run
282+
283+
# should error now
284+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
285+
# --function upgrade_term \
286+
# --type-args $TOKEN_CONTRACT_ID::us::US \
287+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
288+
# --args $DEPOSIT_POOL --args <receipt id> --args 60 \
289+
# --args [20] --args 0x6 --dry-run
290+
291+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
292+
# --function enable_extending_terms \
293+
# --type-args $TOKEN_CONTRACT_ID::us::US \
294+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
295+
# --args $DEPOSIT_POOL --args $ADMIN_CAP --dry-run
296+
297+
# $SUI client call --package $LoyaltyProgramContractID --module deposit_pool \
298+
# --function upgrade_term \
299+
# --type-args $TOKEN_CONTRACT_ID::us::US \
300+
# --type-args $LoyaltyTokenContractID::loyalty::LOYALTY \
301+
# --args $DEPOSIT_POOL --args <receipt id> --args 60 \
302+
# --args [20] --args 0x6 --dry-run

deposit_pool/sources/deposit_pool.move

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const EPendingWithdrawal: u64 = 5;
2929
const ERemovingDefaultTerm: u64 = 6;
3030
/// Error when user choose a term with apy less than expectation;
3131
const EApyMismatched: u64 = 7;
32+
/// Error when user tries to extend term when the pool not support it;
33+
const ENotSupportExtendTerm: u64 = 8;
34+
/// Error when user tries to extend term with wrong parameters;
35+
const EInvalidExtendTerm: u64 = 9;
3236

3337
// ====================== Const =================
3438
/// Current version of the contract
@@ -43,6 +47,9 @@ const KEY_SUPPORT_EARLY_WITHDRAWAL: u8 = 1;
4347
/// Option key for withdrawal pending window (days: u32), optional.
4448
const KEY_WITHDRAWAL_PENDING: u8 = 2;
4549

50+
/// Option key for enable upgrade the term for higher apy (bool), optional.
51+
const KEY_SUPPORT_TERM_EXTENSION: u8 = 3;
52+
4653
public struct AdminCap has key, store {
4754
id: UID,
4855
}
@@ -131,9 +138,9 @@ public fun deposit<Base, Loyalty>(
131138
assert!(pool.version == VERSION, EWrongVersion);
132139

133140
let (lock_term, apy) = if (pool.return_rates.contains(term)) {
134-
(term as u64, *pool.return_rates.borrow(term))
141+
(term as u64, pool.return_rates[term])
135142
} else {
136-
(0, *pool.return_rates.borrow(0))
143+
(0, pool.return_rates[0])
137144
};
138145

139146
// if expected_apy has some value, ensure fetched apy is higer.
@@ -166,7 +173,7 @@ entry fun withdraw<Base, Loyalty>(
166173

167174
// Check eligible for execute withdrawal. Option always exists as this immutable option
168175
// was added during pool creation
169-
if (pool.options.borrow(KEY_SUPPORT_EARLY_WITHDRAWAL)!= true) {
176+
if (!pool.options[KEY_SUPPORT_EARLY_WITHDRAWAL]) {
170177
assert!(clock.timestamp_ms() >= receipt.mature_at_ms, ENotSupportEarlyWithdrawal);
171178
};
172179

@@ -181,7 +188,7 @@ entry fun withdraw<Base, Loyalty>(
181188
df::add(
182189
&mut pool.id,
183190
id(&receipt),
184-
clock.timestamp_ms() + *pool.options.borrow(KEY_WITHDRAWAL_PENDING),
191+
clock.timestamp_ms() + pool.options[KEY_WITHDRAWAL_PENDING],
185192
);
186193
transfer::transfer(receipt, ctx.sender());
187194
return
@@ -215,6 +222,41 @@ entry fun withdraw<Base, Loyalty>(
215222
transfer::public_transfer(pool.balance.split(amount).into_coin(ctx), ctx.sender());
216223
}
217224

225+
/// Upgrade the term of premature deposit receipt if support
226+
public fun upgrade_term<Base, Loyalty>(
227+
pool: &mut DepositPool<Base, Loyalty>,
228+
receipt: &mut Receipt,
229+
target_term: u32,
230+
target_apy: Option<u16>,
231+
clock: &Clock,
232+
) {
233+
assert!(pool.version == VERSION, EWrongVersion);
234+
assert!(receipt.pool_id == object::id(pool), EWrongPool);
235+
236+
// if option does not exists, directly abort
237+
assert!(pool.options.contains(KEY_SUPPORT_TERM_EXTENSION), ENotSupportExtendTerm);
238+
239+
// mature receipt cannot extend, thus if the receipt within withdrwal pending will
240+
// not get additional benefits. An early withdrawal pending will not get any token
241+
// so we can safely ignore the withdrawal pending situation.
242+
assert!(receipt.mature_at_ms > clock.timestamp_ms(), EInvalidExtendTerm);
243+
244+
// new apy must exists
245+
let new_apy = pool.return_rates[target_term];
246+
247+
// To prevent misoperation, if user did not specify the apy, then new apy should be no less than the old one
248+
// However, user may explicilty choose a lower apy.
249+
assert!(new_apy>=target_apy.destroy_or!(receipt.apy), EApyMismatched);
250+
251+
let new_mature_at_ms = receipt.issue_at_ms + (target_term as u64)*MS_PER_DAY;
252+
253+
// mature term should be increase only.
254+
assert!(new_mature_at_ms>receipt.mature_at_ms, EInvalidExtendTerm);
255+
256+
receipt.mature_at_ms = new_mature_at_ms;
257+
receipt.apy = new_apy;
258+
}
259+
218260
/// Updates or adds a new lock term period with corresponding APY
219261
public fun upsert_lock_term<Base, Loyalty>(
220262
pool: &mut DepositPool<Base, Loyalty>,
@@ -257,6 +299,27 @@ public fun delete_lock_term<Base, Loyalty>(
257299
pool.return_rates.remove(days);
258300
}
259301

302+
public fun enable_extending_terms<Base, Loyalty>(
303+
pool: &mut DepositPool<Base, Loyalty>,
304+
admin: &mut AdminCap,
305+
) {
306+
assert!(pool.admin_cap_id == object::id(admin), ENotAdmin);
307+
assert!(pool.version == VERSION, EWrongVersion);
308+
309+
pool.options.add(KEY_SUPPORT_TERM_EXTENSION, true);
310+
}
311+
312+
public fun disable_extending_terms<Base, Loyalty>(
313+
pool: &mut DepositPool<Base, Loyalty>,
314+
admin: &mut AdminCap,
315+
) {
316+
assert!(pool.admin_cap_id == object::id(admin), ENotAdmin);
317+
assert!(pool.version == VERSION, EWrongVersion);
318+
319+
// abort if not exists, then no meaning to disable it.
320+
pool.options.remove<u8, bool>(KEY_SUPPORT_TERM_EXTENSION);
321+
}
322+
260323
/// Adds a new reward pool policy for loyalty tokens
261324
#[allow(lint(self_transfer))]
262325
public fun add_reward_program<Policy: drop, Base, Loyalty>(
@@ -298,7 +361,7 @@ fun calculate_token_amount<Base, Loyalty>(
298361
apy: u16,
299362
): u64 {
300363
let withdraw_at_ms = if (pool.options.contains(KEY_WITHDRAWAL_PENDING)) {
301-
df::remove(&mut pool.id, receipt_id) - *pool.options.borrow(KEY_WITHDRAWAL_PENDING)
364+
df::remove(&mut pool.id, receipt_id) - pool.options[KEY_WITHDRAWAL_PENDING]
302365
} else {
303366
clock.timestamp_ms()
304367
};

0 commit comments

Comments
 (0)