wowok
Version:
Create, collaborate, and transact on your own terms with the AI-driven web3 collaboration protocol.
545 lines (480 loc) • 25.5 kB
text/typescript
import { FnCallType, TxbObject, PermissionObject, PermissionAddress, GuardObject, Protocol, MODULES} from './protocol';
import { array_unique, IsValidAddress, IsValidArray, IsValidDesription, Bcs, IsValidName, IsValidU64} from './utils';
import { ERROR, Errors } from './exception';
import { BCS } from '@mysten/bcs';
import { Transaction as TransactionBlock } from '@mysten/sui/transactions';
export enum PermissionIndex {
repository = 100,
repository_description = 101,
repository_mode = 102,
repository_policies = 103,
repository_reference = 104,
service = 200,
service_description = 201,
service_sales = 202,
service_payee = 203,
service_repository = 204,
service_withdraw_guards = 205,
service_refund_guards = 206,
service_discount_transfer = 207,
service_withdraw = 208,
service_buyer_guard = 209,
service_machine = 210,
service_endpoint = 211,
service_publish = 212,
service_clone = 213,
service_customer_required = 214,
service_pause = 215,
service_treasury = 216,
service_arbitration = 217,
demand = 260,
demand_refund = 261,
demand_expand_time = 262,
demand_guard = 263,
demand_description = 264,
demand_yes = 265,
machine = 600,
machine_description = 601,
machine_repository = 602,
machine_clone = 604,
machine_node = 606,
machine_endpoint = 608,
machine_pause = 609,
machine_publish = 610,
progress = 650,
progress_namedOperator = 651,
progress_bind_task = 652,
progress_context_repository = 653,
progress_unhold = 654,
progress_parent = 655,
treasury = 700,
treasury_receive = 701,
treasury_deposit = 702,
treasury_withdraw = 703,
treasury_descritption = 704,
treasury_deposit_guard = 705,
treasury_withdraw_mode = 706,
treasury_withdraw_guard = 707,
arbitration = 800,
arbitration_description = 801,
arbitration_fee = 802,
arbitration_voting_guard = 803,
arbitration_endpoint = 804,
arbitration_guard = 805,
arbitration_pause = 806,
arbitration_vote = 807,
arbitration_arbitration = 808,
arbitration_withdraw = 809,
arbitration_treasury = 810,
user_defined_start = 1000,
}
export interface PermissionInfoType {
index: number;
name:string;
description:string;
module: string;
guard?: string;
}
export const PermissionInfo : PermissionInfoType[] = [
{index:PermissionIndex.repository, name:'Repository', description:'Launch new Repository', module: MODULES.repository},
{index:PermissionIndex.repository_description, name:'Description', description:'Set Repository description', module: MODULES.repository},
{index:PermissionIndex.repository_mode, name:'Policy mode', description:'Set Repository mode', module: MODULES.repository},
{index:PermissionIndex.repository_policies, name:'Policy', description:'Set Repository policies', module: MODULES.repository},
{index:PermissionIndex.repository_reference, name:'Reference', description:'Set Repository reference', module: MODULES.repository},
{index:PermissionIndex.service, name:'Service', description:'Launch new Service', module: MODULES.service},
{index:PermissionIndex.service_description, name:'Description', description:'Set Service description', module: MODULES.service},
{index:PermissionIndex.service_sales, name:'Sales', description:'Set Service sales items', module: MODULES.service},
{index:PermissionIndex.service_payee, name:'Payee', description:'Set Service payee', module: MODULES.service},
{index:PermissionIndex.service_repository, name:'Repository', description:'Set Service repositories', module: MODULES.service},
{index:PermissionIndex.service_withdraw_guards, name:'Withdraw Guard', description:'Set Service withdraw guards', module: MODULES.service},
{index:PermissionIndex.service_refund_guards, name:'Refund Guard', description:'Set Service refund guards', module: MODULES.service},
{index:PermissionIndex.service_discount_transfer, name:'Discount', description:'Launch discounts for Service', module: MODULES.service},
{index:PermissionIndex.service_buyer_guard, name:'Buyer Guard', description:'Set Guard of buying for Service', module: MODULES.service},
{index:PermissionIndex.service_machine, name:'Machine', description:'Set Machine for Service', module: MODULES.service},
{index:PermissionIndex.service_endpoint, name:'Endpoint', description:'Set Service endpoint', module: MODULES.service},
{index:PermissionIndex.service_publish, name:'Publish', description:'Allowing the creation of Order', module: MODULES.service},
{index:PermissionIndex.service_clone, name:'Clone', description:'Clone Service', module: MODULES.service},
{index:PermissionIndex.service_customer_required, name:'Buyer info', description:'Set Service buyer info required', module: MODULES.service},
{index:PermissionIndex.service_pause, name:'Pause', description:'Pause/Unpause Service', module: MODULES.service},
{index:PermissionIndex.service_treasury, name:'Treasury', description:'Externally withdrawable treasury for compensation or rewards', module: MODULES.service},
{index:PermissionIndex.service_arbitration, name:'Arbitration', description:'Add/Remove arbitration that allows refunds from orders at any time based on arbitration results', module: MODULES.service},
{index:PermissionIndex.demand, name:'Demand', description:'Launch new Demand', module: MODULES.demand},
{index:PermissionIndex.demand_refund, name:'Refund', description:'Refund from Demand', module: MODULES.demand},
{index:PermissionIndex.demand_expand_time, name:'Expand deadline', description:'Expand Demand deadline', module: MODULES.demand},
{index:PermissionIndex.demand_guard, name:'Guard', description:'Set Demand guard', module: MODULES.demand},
{index:PermissionIndex.demand_description, name:'Description', description:'Set Demand description', module: MODULES.demand},
{index:PermissionIndex.demand_yes, name:'Yes', description:'Pick the Deamand serice', module: MODULES.demand},
{index:PermissionIndex.machine, name: 'Machine', description:'Launch new Machine', module: MODULES.machine},
{index:PermissionIndex.machine_description, name: 'Description', description:'Set Machine description', module: MODULES.machine},
{index:PermissionIndex.machine_repository, name: 'Repository', description:'Set Machine repository', module: MODULES.machine},
{index:PermissionIndex.machine_clone, name: 'Clone', description:'Clone Machine', module: MODULES.machine},
{index:PermissionIndex.machine_node, name: 'Node', description:'Set Machine nodes', module: MODULES.machine},
{index:PermissionIndex.machine_endpoint, name: 'Endpoint', description:'Set Machine endpoint', module: MODULES.machine},
{index:PermissionIndex.machine_pause, name: 'Pause', description:'Pause/Unpause Machine', module: MODULES.machine},
{index:PermissionIndex.machine_publish, name: 'Publish', description:'Allowing the creation of Progress', module: MODULES.machine},
{index:PermissionIndex.progress, name: 'Progress', description:'Launch new Progress', module: MODULES.progress},
{index:PermissionIndex.progress_namedOperator, name: 'Operator', description:'Set Progress operators', module: MODULES.progress},
{index:PermissionIndex.progress_bind_task, name: 'Bind', description:'Set Progress task', module: MODULES.progress},
{index:PermissionIndex.progress_context_repository, name: 'Repository', description:'Set Progress repository', module: MODULES.progress},
{index:PermissionIndex.progress_unhold, name: 'Unhold', description:'Release Progress holdings', module: MODULES.progress},
{index:PermissionIndex.progress_parent, name: 'Parent', description:'Set Progress parent', module: MODULES.progress},
{index:PermissionIndex.treasury, name: 'Treasury', description:'Launch new Treasury', module: MODULES.treasury},
{index:PermissionIndex.treasury_deposit, name: 'Deposit', description:'Deposit coins', module: MODULES.treasury},
{index:PermissionIndex.treasury_receive, name: 'Receive', description:'Receive coins from some address sent', module: MODULES.treasury},
{index:PermissionIndex.treasury_withdraw, name: 'Withdraw', description:'Withdraw coins', module: MODULES.treasury},
{index:PermissionIndex.treasury_withdraw_guard, name: 'Withdraw Guard', description:'Add/Remove Treasury withdraw guard', module: MODULES.treasury},
{index:PermissionIndex.treasury_withdraw_mode, name: 'Withdraw mode', description:'Set Treasury withdraw mode', module: MODULES.treasury},
{index:PermissionIndex.treasury_deposit_guard, name: 'Deposit Guard', description:'Set Treasury deposit guard', module: MODULES.treasury},
{index:PermissionIndex.treasury_descritption, name: 'Description', description:'Set Treasury description', module: MODULES.treasury},
{index:PermissionIndex.arbitration, name: 'Arbitration', description:'Launch new Arbitration', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_description, name: 'Description', description:'Set Arbitration description', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_endpoint, name: 'Endpoint', description:'Set Arbitration endpoint', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_fee, name: 'Fee', description:'Set Arbitration fee', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_guard, name: 'Guard', description:'Set Guard to apply for arbitration', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_arbitration, name: 'Arbitrate', description:'Determine the outcome of arbitration', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_pause, name: 'Pause', description:'Allowing/forbidding the creation of Arb', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_voting_guard, name: 'Voting Guard', description:'Add/Remove voting Guard', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_vote, name: 'Vote', description:'Vote on the application for arbitration', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_withdraw, name: 'Withdraw', description:'Withdraw the arbitration fee', module: MODULES.arbitration},
{index:PermissionIndex.arbitration_treasury, name: 'Withdraw', description:'Set Treasury that fees was collected at the time of withdrawal', module: MODULES.arbitration},
]
export interface PermissionAnswer {
who: string;
owner?: boolean;
admin?: boolean;
items?: PermissionAnswerItem[]; // items === undefined, while errors
object: string; // permission object
}
export interface PermissionAnswerItem {
query: PermissionIndexType;
permission: boolean;
guard?: string;
}
export type OnPermissionAnswer = (answer: PermissionAnswer) => void;
export type PermissionIndexType = PermissionIndex | number;
export interface Permission_Entity_Permission {
index: PermissionIndexType;
guard?: TxbObject;
}
export interface Permission_Entity {
address:string;
permissions:Permission_Entity_Permission[];
}
export interface Permission_Index_Entity {
address: string;
guard?: TxbObject;
}
export interface Permission_Index {
index: PermissionIndexType;
entities: Permission_Index_Entity[];
}
export interface BizPermission {
index: PermissionIndexType;
name: string;
}
export class Permission {
protected txb;
protected object : TxbObject;
get_object() { return this.object }
private constructor(txb:TransactionBlock) {
this.txb = txb;
this.object = '';
}
static From(txb:TransactionBlock, object:TxbObject) : Permission {
let p = new Permission(txb);
p.object = Protocol.TXB_OBJECT(txb, object);
return p
}
static New(txb:TransactionBlock, description:string) : Permission {
if (!IsValidDesription(description)) {
ERROR(Errors.IsValidDesription)
}
let p = new Permission(txb);
p.object = txb.moveCall({
target: Protocol.Instance().permissionFn('new') as FnCallType,
arguments: [txb.pure.string(description)]
});
return p
}
launch() : PermissionAddress {
return this.txb.moveCall({ // address returned
target:Protocol.Instance().permissionFn('create') as FnCallType,
arguments:[ Protocol.TXB_OBJECT(this.txb, this.object) ]
})
}
add_bizPermission(index: number, name:string) {
if (!Permission.IsValidBizPermissionIndex(index)) {
ERROR(Errors.IsValidBizPermissionIndex, 'add_bizPermission');
}
if (!IsValidName(name)) {
ERROR(Errors.IsValidName, 'add_bizPermission');
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('user_define_add') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.u64(index), this.txb.pure.string(name)]
})
}
remove_bizPermission(index: number) {
if (!Permission.IsValidBizPermissionIndex(index)) {
ERROR(Errors.IsValidBizPermissionIndex, 'remove_bizPermission');
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('user_define_remove') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.u64(index)]
})
}
transfer_permission(old_entity: string, new_entity: string) {
if (!IsValidAddress(old_entity) || !IsValidAddress(new_entity)) {
ERROR(Errors.IsValidAddress, 'transfer_permission')
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('change_entity') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(old_entity),
this.txb.pure.address(new_entity) ]
})
}
add_entity2(entities: string[], index?:PermissionIndexType) {
if (entities.length === 0) return;
if (!IsValidArray(entities, IsValidAddress)) {
ERROR(Errors.IsValidArray, 'add_entity2');
}
if (index !== undefined) {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('add_with_index') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.u64(index),
this.txb.pure.vector('address', array_unique(entities))]
})
} else {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('add') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.vector('address', array_unique(entities))]
})
}
}
add_entity3(entities: Permission_Index[]) {
if (entities.length === 0) return;
const e : Permission_Entity[] = [];
entities.forEach((v) => {
v.entities.forEach((p) => {
const f = e.find((i) => i.address === p.address);
if (f) {
const t = f.permissions.find((k)=>k.index === v.index);
if (t) {
t.guard = p.guard;
} else {
f.permissions.push({guard:p.guard, index:v.index});
}
} else {
e.push({address:p.address, permissions:[{guard:p.guard, index:v.index}]})
}
})
});
this.add_entity(e);
}
add_entity(entities:Permission_Entity[]) {
if (entities.length === 0) return
let bValid = true;
entities.forEach((v) => {
if (!IsValidAddress(v.address)) bValid = false;
v.permissions.forEach((p) => {
if (!Permission.IsValidPermissionIndex(p.index)) bValid = false;
if (p?.guard && !Protocol.IsValidObjects([p.guard])) bValid = false;
})
});
if (!bValid) {
ERROR(Errors.InvalidParam, 'add_entity.entities');
}
let guards:any[] = [];
for (let i = 0; i < entities.length; i++) {
let entity = entities[i];
let indexes :number[] = [];
for (let j = 0; j < entity.permissions.length; j++) {
let index = entity.permissions[j];
if (!Permission.IsValidPermissionIndex(index.index)) {
continue;
}
if (!indexes.includes(index.index)) {
indexes.push(index.index);
if (index?.guard) {
guards.push({address:entity.address, index:index.index, guard:index.guard});
}
}
}
if (indexes.length > 0) {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('add_batch') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(entity.address),
this.txb.pure.vector('u64', indexes)]
})
}
}
// set guards
guards.forEach(({address, index, guard}) => {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('guard_set') as FnCallType,
arguments:[ Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(address),
this.txb.pure.u64(index), Protocol.TXB_OBJECT(this.txb, guard)]
})
})
}
// guard: undefine to set none
set_guard(address:string, index:PermissionIndexType, guard?:GuardObject) {
if (!IsValidAddress(address)) {
ERROR(Errors.IsValidAddress, 'address')
}
if(!Permission.IsValidPermissionIndex(index) && !Permission.IsValidBizPermissionIndex(index)) {
ERROR(Errors.IsValidPermissionIndex, 'index')
}
if (guard) {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('guard_set') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(address),
this.txb.pure.u64(index), Protocol.TXB_OBJECT(this.txb, guard)]
})
} else {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('guard_none') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(address),
this.txb.pure.u64(index)]
})
};
}
remove_index(address:string, index:PermissionIndexType[]) {
if (!IsValidAddress(address)) {
ERROR(Errors.IsValidAddress)
}
if (index.length === 0) return ;
if (!(IsValidArray(index, Permission.IsValidPermissionIndex))) {
ERROR(Errors.InvalidParam, 'index')
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('remove_index') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(address),
this.txb.pure.vector('u64', array_unique(index))]
})
}
remove_entity(address:string[]) {
if (address.length === 0) return ;
if (!IsValidArray(address, IsValidAddress)) {
ERROR(Errors.IsValidArray)
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('remove') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.vector('address', array_unique(address))]
})
}
set_description(description:string) {
if (!IsValidDesription(description)) {
ERROR(Errors.IsValidDesription)
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('description_set') as FnCallType,
arguments: [Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.string(description)]
})
;
}
add_admin(admin:string[]) {
if (admin.length === 0) return ;
if (!IsValidArray(admin, IsValidAddress)) {
ERROR(Errors.IsValidArray)
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('admin_add_batch') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.vector('address', array_unique(admin))]
});
}
remove_admin(admin:string[], removeall?:boolean) {
if (!removeall && admin.length === 0) return;
if (!IsValidArray(admin, IsValidAddress)) {
ERROR(Errors.IsValidArray, 'admin')
}
if (removeall) {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('admins_clear') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object)]
});
} else if (admin) {
this.txb.moveCall({
target:Protocol.Instance().permissionFn('admin_remove_batch') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.vector('address', array_unique(admin))]
});
}
}
change_owner(new_owner:string) {
if (!IsValidAddress(new_owner)) {
ERROR(Errors.IsValidAddress)
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('builder_set') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(new_owner)]
});
}
// query all permissions for address
query_permissions_all(address_queried:string) {
if (!IsValidAddress(address_queried)) {
ERROR(Errors.InvalidParam, 'query_permissions');
}
this.txb.moveCall({
target:Protocol.Instance().permissionFn('query_permissions_all') as FnCallType,
arguments:[Protocol.TXB_OBJECT(this.txb, this.object), this.txb.pure.address(address_queried)]
})
}
QueryPermissions(permission:string, address_queried:string, onPermissionAnswer:OnPermissionAnswer, sender?:string) {
//@ be the same txb
this.query_permissions_all(address_queried);
//console.log(address_queried)
Protocol.Client().devInspectTransactionBlock({sender:sender ?? address_queried, transactionBlock:this.txb}).then((res) => {
if (res.results && res.results[0].returnValues && res.results[0].returnValues.length !== 2) {
onPermissionAnswer({who:address_queried, object:permission});
return
}
const perm = Bcs.getInstance().de(BCS.U8, Uint8Array.from((res.results as any)[0].returnValues[0][0]));
if (perm === Permission.PERMISSION_ADMIN || perm === Permission.PERMISSION_OWNER_AND_ADMIN) {
onPermissionAnswer({who:address_queried, admin:true, owner:perm%2===1, items:[], object:permission})
} else {
const perms = Bcs.getInstance().de_perms(Uint8Array.from((res.results as any)[0].returnValues[1][0]));
onPermissionAnswer({who:address_queried, admin:false, owner:perm%2===1, items:perms.map((v:any)=>{
return {query:v?.index, permission:true, guard:v?.guard}
}), object:permission});
}
}).catch((e) => {
console.log(e);
onPermissionAnswer({who:address_queried, object:permission});
})
}
static HasPermission(answer:PermissionAnswer|undefined, index:PermissionIndexType, bStrict:boolean=false) : {has:boolean, guard?:string, owner?:boolean} | undefined {
if (answer) {
if (answer.admin) return {has:true, owner:answer.owner}; // admin
let i = answer.items?.find((v)=>v.query == index); // index maybe string, so ==
if (i) {
return {has:i.permission, guard:i.guard, owner:answer.owner};
} else {
return {has:false, guard:undefined, owner:answer?.owner}
}
}
if (bStrict) {
return {has:false, guard:undefined, owner:false}
}
return undefined // basic: !== false ; otherwise: !
}
static MAX_ADMIN_COUNT = 64;
static MAX_ENTITY_COUNT = 2000;
static MAX_PERMISSION_INDEX_COUNT = 200;
static MAX_PERSONAL_PERMISSION_COUNT = 200;
static PERMISSION_NORMAL = 0;
static PERMISSION_OWNER = 1;
static PERMISSION_ADMIN = 2;
static PERMISSION_OWNER_AND_ADMIN = 3;
static BUSINESS_PERMISSIONS_START = PermissionIndex.user_defined_start;
static IsValidBizPermissionIndex = (index:number) => {
return index >= Permission.BUSINESS_PERMISSIONS_START && IsValidU64(index)
}
static IsValidPermissionIndex = (index:PermissionIndexType) : boolean => {
//console.log(index)
if (Object.values(PermissionIndex).includes(index)) {
return true
}
//console.log(Object.keys(PermissionIndex))
return Permission.IsValidBizPermissionIndex(index);
}
}