Skip to content

Commit

Permalink
Handy admin coupon management + action (#149)
Browse files Browse the repository at this point in the history
* Add coupon winners action

* Fix years

* nits
  • Loading branch information
manolisliolios authored Jul 17, 2024
1 parent 15d58c7 commit b4d00c3
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/suins-build-tx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- Profits to Treasury
- Transfer Reserved Names
- DayOne Discount
- Contest Winner Coupons
sui_tools_image:
description: 'image reference of sui_tools'
default: 'mysten/sui-tools:mainnet'
Expand Down Expand Up @@ -122,6 +123,16 @@ jobs:
run: |
cd scripts && pnpm setup-dayone-discount
- name: Contest Winner Coupons
if: ${{ inputs.transaction_type == 'Contest Winner Coupons' }}
env:
NODE_ENV: production
GAS_OBJECT: ${{ inputs.gas_object_id }}
NETWORK: mainnet
ORIGIN: gh_action
run: |
cd scripts && pnpm ts-node transactions/create-contest-winner-coupons.ts
- name: Show Transaction Data (To sign)
run: |
cat scripts/tx/tx-data.txt
Expand Down
166 changes: 166 additions & 0 deletions scripts/coupons/coupon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
import { TransactionBlock } from '@mysten/sui.js/transactions';
import { isValidSuiAddress } from '@mysten/sui.js/utils';

import { PackageInfo } from '../config/constants';

export class CouponType {
name?: string;
type?: number;
value: string | number | bigint;
rules: CouponRules;

constructor(value: string | number | bigint, type?: number) {
this.value = value;
this.type = type;
this.rules = {};
}

/**
* Accepts a range for characters.
* Use plain number for fixed length. (e.g. `setLengthRule(3)`)
* Use range for specific lengths. Max length = 63, min length = 3
*/
setLengthRule(range: number[] | number) {
if (typeof range === 'number') range = [range, range];
if (range.length !== 2 || range[1] < range[0] || range[0] < 3 || range[1] > 63)
throw new Error('Range has to be 2 numbers, from smaller to number, between [3,63]');
this.rules.length = range;
return this;
}

setAvailableClaims(claims: number) {
this.rules.availableClaims = claims;
return this;
}

setUser(user: string) {
if (!isValidSuiAddress(user)) throw new Error('Invalid address for user.');
this.rules.user = user;
return this;
}

setExpiration(timestamp_ms: string) {
this.rules.expiration = timestamp_ms;
return this;
}

/**
* Accepts a range for years, between [1,5]
*/
setYears(range: number[]) {
if (range.length !== 2 || range[1] < range[0] || range[0] < 1 || range[1] > 5)
throw new Error('Range has to be 2 numbers, from smaller to number, between [1,5]');
this.rules.years = range;
return this;
}

setName(name: string) {
this.name = name;
return this;
}

/**
* Converts the coupon to a transaction.
*/
toTransaction(txb: TransactionBlock, config: PackageInfo): TransactionBlock {
if (this.type === undefined) throw new Error('You have to define a type');
if (!this.name) throw new Error('Please define a name for the coupon');

let adminCap = txb.object(config.adminCap);

let lengthRule = optionalRangeConstructor(txb, config, this.rules.length);
let yearsRule = optionalRangeConstructor(txb, config, this.rules.years);

let rules = txb.moveCall({
target: `${config.coupons.packageId}::rules::new_coupon_rules`,
arguments: [
lengthRule,
this.rules.availableClaims
? filledOption(txb, this.rules.availableClaims, 'u64')
: emptyOption(txb, 'u64'),
this.rules.user
? filledOption(txb, this.rules.user, 'address')
: emptyOption(txb, 'address'),
this.rules.expiration
? filledOption(txb, this.rules.expiration, 'u64')
: emptyOption(txb, 'u64'),
yearsRule,
],
});

txb.moveCall({
target: `${config.coupons.packageId}::coupon_house::admin_add_coupon`,
arguments: [
adminCap,
txb.object(config.suins),
txb.pure.string(this.name),
txb.pure.u8(this.type),
txb.pure.u64(this.value),
rules,
],
});

return txb;
}
}

export class FixedPriceCoupon extends CouponType {
constructor(value: string | number | bigint) {
super(value, 1);
}
}
export class PercentageOffCoupon extends CouponType {
constructor(value: string | number | bigint) {
if (Number(value) <= 0 || Number(value) > 100)
throw new Error('Percentage discount can be in (0, 100] range, 0 exclusive.');
super(value, 0);
}
}

export type CouponRules = {
length?: number[];
availableClaims?: number;
user?: string;
expiration?: string;
years?: number[];
};

const emptyOption = (txb: TransactionBlock, type: string) => {
return txb.pure(
{
None: true,
},
`Option<${type}>`,
);
};

const filledOption = (txb: TransactionBlock, value: any, type: string) => {
return txb.pure(
{
Some: value,
},
`Option<${type}>`,
);
};

const optionalRangeConstructor = (txb: TransactionBlock, config: PackageInfo, range?: number[]) => {
if (!range)
return txb.moveCall({
target: '0x1::option::none',
typeArguments: [`${config.coupons.packageId}::range::Range`],
arguments: [],
});

let rangeArg = txb.moveCall({
target: `${config.coupons.packageId}::range::new`,
arguments: [txb.pure(range[0], 'u8'), txb.pure(range[1], 'u8')],
});

return txb.moveCall({
target: '0x1::option::some',
typeArguments: [`${config.coupons.packageId}::range::Range`],
arguments: [rangeArg],
});
};
41 changes: 41 additions & 0 deletions scripts/transactions/create-contest-winner-coupons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
import { TransactionBlock } from '@mysten/sui.js/transactions';

import { mainPackage } from '../config/constants';
import { PercentageOffCoupon } from '../coupons/coupon';
import { prepareMultisigTx } from '../utils/utils';

const create = async () => {
const pkg = mainPackage.mainnet;

const tx = new TransactionBlock();

new PercentageOffCoupon(100)
.setName('0x88528ee645ca295e618fec3ad8735d79712a7c4964796717ac0dc2651261f795')
.setAvailableClaims(1)
.setLengthRule([4, 63])
.setUser('0x88528ee645ca295e618fec3ad8735d79712a7c4964796717ac0dc2651261f795')
.setYears([1, 1])
.toTransaction(tx, pkg);

new PercentageOffCoupon(100)
.setName('0xda4e42546326a001086b70828e507ffe7a745e85cdc4bb1b25b52e54749999f4')
.setAvailableClaims(1)
.setLengthRule([4, 63])
.setUser('0xda4e42546326a001086b70828e507ffe7a745e85cdc4bb1b25b52e54749999f4')
.setYears([1, 1])
.toTransaction(tx, pkg);

new PercentageOffCoupon(100)
.setName('0x74fed224663d295ba11e22522c53d76b09f7fa6ed1cce7ecf866c8edf417666e')
.setAvailableClaims(1)
.setLengthRule([4, 63])
.setUser('0x74fed224663d295ba11e22522c53d76b09f7fa6ed1cce7ecf866c8edf417666e')
.setYears([1, 1])
.toTransaction(tx, pkg);

await prepareMultisigTx(tx, 'mainnet', pkg.adminAddress);
};

create();

0 comments on commit b4d00c3

Please sign in to comment.