UNPKG

wowok

Version:

Create, collaborate, and transact on your own terms with the AI-driven web3 collaboration protocol.

669 lines (608 loc) 23.5 kB
import { BCS, getSuiMoveConfig, toHEX, } from '@mysten/bcs'; import { SuiObjectResponse, DynamicFieldPage } from '@mysten/sui/client'; import { ERROR, Errors } from './exception'; import { isValidSuiAddress, normalizeSuiAddress} from '@mysten/sui/utils' import { RepositoryValueType, ValueType, Protocol, ContextType, OperatorType } from './protocol' export const MAX_U8 = BigInt('255'); export const MAX_U64 = BigInt('18446744073709551615'); export const MAX_U128 = BigInt('340282366920938463463374607431768211455'); export const MAX_U256 = BigInt('115792089237316195423570985008687907853269984665640564039457584007913129639935'); export const OPTION_NONE = 0; export const ValueTypeConvert = (type:ValueType | null | undefined) : RepositoryValueType | number => { if (type === ValueType.TYPE_U8 || type === ValueType.TYPE_U64 || type === ValueType.TYPE_U128 || type === ValueType.TYPE_U256) { return RepositoryValueType.PositiveNumber } else if (type === ValueType.TYPE_VEC_U8 || type === ValueType.TYPE_VEC_U64 || type === ValueType.TYPE_VEC_U128 || type === ValueType.TYPE_VEC_U256|| type === ValueType.TYPE_VEC_BOOL) { return RepositoryValueType.PositiveNumber_Vec } else if (type === ValueType.TYPE_ADDRESS) { return RepositoryValueType.Address } else if (type === ValueType.TYPE_VEC_ADDRESS) { return RepositoryValueType.Address_Vec } else if (type === ValueType.TYPE_STRING) { return RepositoryValueType.String } else if (type === ValueType.TYPE_VEC_STRING) { return RepositoryValueType.String_Vec } else if (type === ValueType.TYPE_BOOL) { return RepositoryValueType.Bool } return -1; } export const readOption = (arr: number[], de:ValueType) : {bNone:boolean, value:any} => { let o = arr.splice(0, 1); if (o[0] == 1) { // true return {bNone:false, value:Bcs.getInstance().de(de, Uint8Array.from(arr))}; } else if (o[0] == 0) { return {bNone:true, value:OPTION_NONE}; } else { ERROR(Errors.Fail, 'readOption: option invalid') return {bNone:true, value:OPTION_NONE} } } export const readOptionString = (arr: number[]) : {bNone:boolean, value:any}=> { let o = arr.splice(0, 1); if (o[0] == 1) { // true let r = ulebDecode(Uint8Array.from(arr)); let value = Bcs.getInstance().de(ValueType.TYPE_STRING, Uint8Array.from(arr)); arr.splice(0, r.value+r.length); return {bNone:false, value:value}; } else if (o[0] == 0) { return {bNone:true, value:OPTION_NONE}; } else { ERROR(Errors.Fail, 'readOption: option invalid') return {bNone:true, value:OPTION_NONE} } } export const ulebDecode = (arr: Uint8Array) : {value: number, length: number} => { let total = 0; let shift = 0; let len = 0; // eslint-disable-next-line no-constant-condition while (true) { let byte = arr[len]; len += 1; total |= (byte & 0x7f) << shift; if ((byte & 0x80) === 0) { break; } shift += 7; } return { value: total, length: len, }; } export const readVec = (arr: any[], cb:(arr:any[], i:number, length:number) => any) : any[] => { let r = ulebDecode(Uint8Array.from(arr)); arr.splice(0, r.length) ; let result = []; for (let i = 0; i < r.value; i++) { result.push(cb(arr, i, r.value)); } return result; } export const cb_U8 = (arr:any[], i:number, length:number) : any => { return arr.shift(); } export const cb_U64 = (arr:any[], i:number, length:number) : any => { return arr.splice(0, 8); } export const cb_U128 = (arr:any[], i:number, length:number) : any => { return arr.splice(0, 16); } export const cb_U256 = (arr:any[], i:number, length:number) : any => { return arr.splice(0, 32); } export const concatenate = (resultConstructor:any, ...arrays:any[]) => { let totalLength = 0; for (const arr of arrays) { totalLength += arr.length; } const result = new resultConstructor(totalLength); let offset = 0; for (const arr of arrays) { result.set(arr, offset); offset += arr.length; } return result; } export const parseObjectType = (chain_type:string | null | undefined, header:string='payment::Payment<') : string => { if (chain_type) { const i = chain_type.indexOf(header); if (i > 0) { let r = chain_type.slice(i + header.length, chain_type.length-1); return r } } return ''; } export const array_equal = (arr1: any[], arr2: any[]) => { if (arr1.length !== arr2.length) { return false; } return !arr1.some((item) => !arr2.includes(item)); } export const array_unique = (arr:any[]) : any[] => { var newArr = []; for(var i = 0; i < arr.length; i++) { if(newArr.indexOf(arr[i]) == -1) { newArr.push(arr[i]); } } return newArr; } export function capitalize (s:string) : string { return s && s[0].toUpperCase() + s.slice(1) } // for: "0xsdjfkskf<0x2::sui::coin<xxx>, 0xfdfff<>>" export function parse_object_type(object_data:string) : string[] { var object_type : string[] = []; let type_pos = object_data.indexOf('<'); if (type_pos >= 0) { let t = object_data.slice((type_pos+1), object_data.length-1); object_type = t.split(','); } return object_type; } export class Bcs { bcs = new BCS(getSuiMoveConfig()); private static _instance : any; private constructor() { this.bcs.registerEnumType('Option<T>', { 'none': null, 'some': 'T', }); this.bcs.registerStructType('EntStruct', { 'avatar': 'vector<u8>', 'resource': "Option<address>", "safer_name": "vector<string>", "safer_value": "vector<string>", 'like': BCS.U32, 'dislike': BCS.U32, }); this.bcs.registerStructType('TagStruct', { 'nick': 'string', 'tags': "vector<string>", }) this.bcs.registerStructType('PersonalInfo', { 'name': 'vector<u8>', 'description': 'vector<u8>', 'avatar': BCS.STRING, 'twitter': BCS.STRING, 'discord': BCS.STRING, 'homepage': BCS.STRING, }) this.bcs.registerStructType('OptionAddress', { 'address': 'Option<address>', }) this.bcs.registerStructType('Guards', { 'guards':'vector<OptionAddress>', }) this.bcs.registerStructType('Perm', { 'index': BCS.U64, 'guard': 'Option<address>' }) this.bcs.registerStructType('Perms', { 'perms':'vector<Perm>' }) } static getInstance() : Bcs { if (!Bcs._instance) { Bcs._instance = new Bcs(); }; return Bcs._instance; } ser_option_u32(data:Uint8Array | any) : Uint8Array { return this.bcs.ser('Option<u32>', {'some': data}).toBytes(); } ser(type:ValueType | ContextType | string, data:Uint8Array | any) : Uint8Array { if (typeof(type) === 'string') { return this.bcs.ser(type, data).toBytes(); } switch(type) { case ValueType.TYPE_BOOL: return this.bcs.ser(BCS.BOOL, data).toBytes(); case ValueType.TYPE_ADDRESS: return this.bcs.ser(BCS.ADDRESS, data).toBytes(); case ValueType.TYPE_U64: return this.bcs.ser(BCS.U64, data).toBytes(); case ValueType.TYPE_U8: return this.bcs.ser(BCS.U8, data).toBytes(); case ValueType.TYPE_VEC_U8: return this.bcs.ser('vector<u8>', data).toBytes(); case ValueType.TYPE_U128: return this.bcs.ser(BCS.U128, data).toBytes(); case ValueType.TYPE_VEC_ADDRESS: return this.bcs.ser('vector<address>', data).toBytes(); case ValueType.TYPE_VEC_BOOL: return this.bcs.ser('vector<bool>', data).toBytes(); case ValueType.TYPE_VEC_VEC_U8: return this.bcs.ser('vector<vector<u8>>', data).toBytes(); case ValueType.TYPE_VEC_U64: return this.bcs.ser('vector<u64>', data).toBytes(); case ValueType.TYPE_VEC_U128: return this.bcs.ser('vector<u128>', data).toBytes(); case ValueType.TYPE_OPTION_ADDRESS: return this.bcs.ser('Option<address>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_BOOL: return this.bcs.ser('Option<bool>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_U8: return this.bcs.ser('Option<u8>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_U64: return this.bcs.ser('Option<u64>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_U128: return this.bcs.ser('Option<u128>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_U256: return this.bcs.ser('Option<u256>', {'some': data}).toBytes(); case ValueType.TYPE_OPTION_STRING: return this.bcs.ser('Option<string>', {'some': data}).toBytes(); case ValueType.TYPE_VEC_U256: return this.bcs.ser('vector<u256>', data).toBytes(); case ValueType.TYPE_U256: return this.bcs.ser(BCS.U256, data).toBytes(); case ValueType.TYPE_STRING: const d = new TextEncoder().encode(data); return this.bcs.ser('vector<u8>', d).toBytes(); case ValueType.TYPE_VEC_STRING: return this.bcs.ser('vector<vector<u8>>', data.map((v:string)=>{return new TextEncoder().encode(v)})).toBytes(); default: ERROR(Errors.bcsTypeInvalid, 'ser'); } return new Uint8Array(); } de(type:ValueType | string, data:Uint8Array | any) : any { if (typeof(type) === 'string') { return this.bcs.de(type, data); } switch(type) { case ValueType.TYPE_BOOL: return this.bcs.de(BCS.BOOL, data); case ValueType.TYPE_ADDRESS: return this.bcs.de(BCS.ADDRESS, data); case ValueType.TYPE_U64: return this.bcs.de(BCS.U64, data); case ValueType.TYPE_U8: return this.bcs.de(BCS.U8, data); case ValueType.TYPE_VEC_U8: return this.bcs.de('vector<u8>', data); case ValueType.TYPE_U128: return this.bcs.de(BCS.U128, data); case ValueType.TYPE_VEC_ADDRESS: return this.bcs.de('vector<address>', data); case ValueType.TYPE_VEC_BOOL: return this.bcs.de('vector<bool>', data); case ValueType.TYPE_VEC_VEC_U8: return this.bcs.de('vector<vector<u8>>', data); case ValueType.TYPE_VEC_U64: return this.bcs.de('vector<u64>', data); case ValueType.TYPE_VEC_U128: return this.bcs.de('vector<u128>', data); case ValueType.TYPE_OPTION_ADDRESS: return this.bcs.de('Option<address>', data); case ValueType.TYPE_OPTION_BOOL: return this.bcs.de('Option<bool>', data); case ValueType.TYPE_OPTION_U8: return this.bcs.de('Option<u8>', data); case ValueType.TYPE_OPTION_U64: return this.bcs.de('Option<u64>', data); case ValueType.TYPE_OPTION_U128: return this.bcs.de('Option<u128>', data); case ValueType.TYPE_OPTION_U256: return this.bcs.de('Option<u256>', data); case ValueType.TYPE_OPTION_STRING: return this.bcs.de('Option<string>', data); case ValueType.TYPE_VEC_U256: return this.bcs.de('vector<u256>', data); case ValueType.TYPE_STRING: const r = new TextDecoder().decode(Uint8Array.from(this.bcs.de('vector<u8>', data))); return r case ValueType.TYPE_VEC_STRING: return this.bcs.de('vector<string>', data); case ValueType.TYPE_U256: return this.bcs.de(BCS.U256, data); default: ERROR(Errors.bcsTypeInvalid, 'de'); } } de_ent(data:Uint8Array | undefined) : any { if (!data || data.length < 2) return '' const struct_vec = this.bcs.de('vector<u8>', data); return this.bcs.de('EntStruct', Uint8Array.from(struct_vec)); } de_entInfo(data:Uint8Array | undefined) : any { if (!data || data.length === 0) return undefined let r = this.bcs.de('PersonalInfo', data); r.name = new TextDecoder().decode(Uint8Array.from(r.name)); r.description = new TextDecoder().decode(Uint8Array.from(r.description)); return r } de_tags(data:Uint8Array | undefined) : any { if (!data || data.length === 0) return '' const struct_vec = this.bcs.de('vector<u8>', data); return this.bcs.de('TagStruct', Uint8Array.from(struct_vec)); } de_perms(data:Uint8Array | undefined) : any { if (!data || data.length < 1) return '' let r = this.bcs.de('Perms', data); return r.perms.map((v:any) => { return {index: v?.index, guard:v?.guard?.none ? undefined : '0x'+v?.guard?.some} }) } } export function stringToUint8Array(str:string) { const encoder = new TextEncoder(); const view = encoder.encode(str); return new Uint8Array(view.buffer); } export function numToUint8Array(num:number) : Uint8Array { if (!num) return new Uint8Array(0) const a = []; a.unshift(num & 255) while (num >= 256) { num = num >>> 8 a.unshift(num & 255) } return new Uint8Array(a) } export const isArr = (origin: any): boolean => { let str = '[object Array]' return Object.prototype.toString.call(origin) == str ? true : false } export const deepClone = <T>(origin: T, target?: Record<string, any> | T ): T => { let tar = target || {} for (const key in origin) { if (Object.prototype.hasOwnProperty.call(origin, key)) { if (typeof origin[key] === 'object' && origin[key] !== null) { tar[key] = isArr(origin[key]) ? [] : {} deepClone(origin[key], tar[key]) } else { tar[key] = origin[key] } } } return tar as T } export const MAX_DESCRIPTION_LENGTH = 4000; export const MAX_NAME_LENGTH = 64; export const MAX_ENDPOINT_LENGTH = 1024; // export const OptionNone = (txb:TransactionBlock) : TransactionArgument => { return txb.pure([], BCS.U8) }; const IsValidStringLength = (str: string, max_len:number) : boolean => { return Bcs.getInstance().ser(ValueType.TYPE_STRING, str).length <= max_len } export const IsValidDesription = (description:string) : boolean => { return IsValidStringLength(description, MAX_DESCRIPTION_LENGTH) } export const IsValidName = (name?:string) : boolean => { if(!name || name.length === 0) { return false; } return IsValidStringLength(name, MAX_NAME_LENGTH) } export const IsValidName_AllowEmpty = (name:string) : boolean => { return IsValidStringLength(name, MAX_NAME_LENGTH)} export const IsValidEndpoint = (endpoint:string) : boolean => { return (endpoint.length > 0 && endpoint.length <= MAX_ENDPOINT_LENGTH && isValidHttpUrl(endpoint)) ; } export const IsValidAddress = (addr:string | undefined) : boolean => { if (!addr || !isValidSuiAddress(addr)) { return false; } return true } export const IsValidCoinType = (coin_type:string | undefined) : boolean => { if (!coin_type) { return false; } return coin_type.startsWith('0x2::coin::Coin') || coin_type.startsWith('0x0000000000000000000000000000000000000000000000000000000000000002') } export const getUTCDayStartByDivision = (interval=86400000): number => { // 1 day default const now = Date.now(); return Math.floor(now / interval) * interval; } export const IsValidBigint = (value:string | number | undefined | bigint, max:bigint=MAX_U256, min?:bigint) : boolean => { if (value === '' || value === undefined) return false; try { const v = BigInt(value); if (v <= max) { if (min !== undefined) { return v >= min; } return true } } catch (e) { }; return false } export const IsValidU8 = (value:string | number | undefined | bigint, min=0) : boolean => { return IsValidBigint(value, MAX_U8, BigInt(min)) } export const IsValidU64 = (value:string | number | undefined | bigint, min=0) : boolean => { return IsValidBigint(value, MAX_U64, BigInt(min)) } export const IsValidU128 = (value:string | number | undefined | bigint, min=0) : boolean => { return IsValidBigint(value, MAX_U128, BigInt(min)) } export const IsValidU256 = (value:string | number | undefined | bigint, min=0) : boolean => { return IsValidBigint(value, MAX_U256, BigInt(min)) } export const IsValidTokenType = (argType: string) : boolean => { if (!argType || argType.length === 0) { return false; } let arr = argType.split('::'); if (arr.length !== 3) { return false; } if ((!IsValidAddress(arr[0]) && arr[0] != '0x2') || arr[1].length === 0 || arr[2].length === 0) { return false; } return true; } export const IsValidArgType = (argType: string) : boolean => { if (!argType || argType.length === 0) { return false; } let arr = argType.split('::'); if (arr.length < 3) { return false; } return true; } export const IsValidInt = (value: number | string) : boolean => { if (typeof(value) === 'string') { value = parseInt(value as string); } return Number.isSafeInteger(value) } export const IsValidPercent = (value: number | string | bigint) : boolean => { return IsValidBigint(value, BigInt(100), BigInt(0)) } export const IsValidArray = (arr: any, validFunc:any) : boolean => { for (let i = 0; i < arr.length; i++) { if (!validFunc(arr[i])) { return false } } return true } export const ResolveU64 = (value:bigint) : bigint => { const max = MAX_U64; if (value > max) { return max; } else { return value } } function removeTrailingZeros(numberString: string): string { const trimmedString = numberString.trim(); const decimalIndex = trimmedString.indexOf('.'); if (decimalIndex !== -1) { let endIndex = trimmedString.length - 1; while (trimmedString[endIndex] === '0') { endIndex--; } if (trimmedString[endIndex] === '.') { endIndex--; } return trimmedString.slice(0, endIndex + 1); } return trimmedString; } export const ResolveBalance = (balance:string, decimals:number) : string => { if (!balance) return '' if (balance === '0') return '0' if (decimals <= 0) return balance; var pos = decimals - balance.length; if (pos === 0) { return removeTrailingZeros('.' + (balance)); } else if (pos < 0) { let start = balance.slice(0, Math.abs(pos)); let end = balance.slice(Math.abs(pos)); return removeTrailingZeros(start + '.' + end); } else { return removeTrailingZeros('.' + balance.padStart(decimals, '0')); } } export type ArgType = { isCoin: boolean; coin: string; token: string; } export const ParseType = (type:string) : ArgType => { if (type) { const COIN = '0x2::coin::Coin<'; let i = type.indexOf(COIN); if (i >= 0) { let coin = type.slice(i+COIN.length, type.length-1); if (coin.indexOf('<') === -1) { while (coin[coin.length-1] == '>') { coin = coin.slice(0, -1); }; let t = coin.lastIndexOf('::'); return {isCoin:true, coin:coin, token:coin.slice(t+2)} } } } return {isCoin:false, coin:'', token:''} } export function insertAtHead(array:Uint8Array, value:number) { const newLength = array.length + 1; const newArray = new Uint8Array(newLength); newArray.set([value], 0); newArray.set(array, 1); return newArray; } export function toFixed(x:number) { let res = ''; if (Math.abs(x) < 1.0) { var e = parseInt(x.toString().split('e-')[1]); if (e) { x *= Math.pow(10,e-1); res = '0.' + (new Array(e)).join('0') + x.toString().substring(2); } } else { var e = parseInt(x.toString().split('+')[1]); if (e > 20) { e -= 20; x /= Math.pow(10,e); res = x + (new Array(e+1)).join('0'); } } return res; } export function isValidHttpUrl(url:string) : boolean { let r:any; try { r = new URL(url); } catch (_) { return false; } return r.protocol === "http:" || r.protocol === "https:" || r.protocol === 'ipfs:'; } export interface query_object_param { id:string; onBegin?:(id:string)=>void; onObjectRes?:(id:string, res:SuiObjectResponse)=>void; onDynamicRes?:(id:string, res:DynamicFieldPage)=>void; onFieldsRes?:(id:string, fields_res:SuiObjectResponse[])=>void; onObjectErr?:(id:string, err:any)=>void; onDynamicErr?:(id:string, err:any)=>void; onFieldsErr?:(id:string, err:any)=>void; } export const uint2address = (value: number | bigint) : string => { return normalizeSuiAddress(value.toString(16)); } export const query_object = (param:query_object_param) => { if (param.id) { if(param?.onBegin) param.onBegin(param.id); Protocol.Client().getObject({id:param.id, options:{showContent:true, showType:true, showOwner:true}}).then((res) => { if (res.error) { if(param?.onObjectErr) param.onObjectErr(param.id, res.error); } else { if(param?.onObjectRes) param.onObjectRes(param.id, res); } }).catch((err) => { console.log(err) if (param?.onObjectErr) param.onObjectErr(param.id, err); }); Protocol.Client().getDynamicFields({parentId:param.id}).then((res) => { if (param?.onDynamicRes) param.onDynamicRes(param.id, res); if (res.data.length > 0) { Protocol.Client().multiGetObjects({ids:res.data.map(v => v.objectId), options:{showContent:true}}).then((fields) => { if (param?.onFieldsRes) param.onFieldsRes(param.id, fields); }).catch((err) => { console.log(err) if (param?.onFieldsErr) param.onFieldsErr(param.id, err); }) } }).catch((err) => { console.log(err) if (param?.onDynamicErr) param.onDynamicErr(param.id, err); }) } } export const FirstLetterUppercase = (str:string|undefined|null) : string => { if (!str) return ''; return str.substring(0, 1).toUpperCase() + str.substring(1); } export function hasDuplicates<T>(array: T[]): boolean { return array.some((item, index) => array.findIndex(i => i === item) !== index); }