PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-sui/src/lib/mystenlab/builder
Просмотр файла: TransactionDataBlock.ts
import { toB58 } from '@mysten/bcs';
import {
array,
assert,
define,
Infer,
integer,
is,
literal,
nullable,
object,
optional,
string,
union,
} from 'superstruct';
import { hashTypedData } from '../cryptography/hash';
import { normalizeSuiAddress, SuiObjectRef } from '../types';
import { builder } from './bcs';
import { TransactionType, TransactionBlockInput } from './Transactions';
import { BuilderCallArg, PureCallArg } from './Inputs';
import { create } from './utils';
export const TransactionExpiration = optional(
nullable(union([object({ Epoch: integer() }), object({ None: union([literal(true), literal(null)]) })]))
);
export type TransactionExpiration = Infer<typeof TransactionExpiration>;
const SuiAddress = string();
const StringEncodedBigint = define<string>('StringEncodedBigint', (val) => {
if (!['string', 'number', 'bigint'].includes(typeof val)) return false;
try {
BigInt(val as string);
return true;
} catch {
return false;
}
});
const GasConfig = object({
budget: optional(StringEncodedBigint),
price: optional(StringEncodedBigint),
payment: optional(array(SuiObjectRef)),
owner: optional(SuiAddress),
});
type GasConfig = Infer<typeof GasConfig>;
export const SerializedTransactionDataBuilder = object({
version: literal(1),
sender: optional(SuiAddress),
expiration: TransactionExpiration,
gasConfig: GasConfig,
inputs: array(TransactionBlockInput),
transactions: array(TransactionType),
});
export type SerializedTransactionDataBuilder = Infer<typeof SerializedTransactionDataBuilder>;
function prepareSuiAddress(address: string) {
return normalizeSuiAddress(address).replace('0x', '');
}
// NOTE: This value should be kept in sync with the corresponding value in
// crates/sui-protocol-config/src/lib.rs
export const TRANSACTION_DATA_MAX_SIZE = 128 * 1024;
export class TransactionBlockDataBuilder {
static fromKindBytes(bytes: Uint8Array) {
const kind = builder.de('TransactionKind', bytes);
const programmableTx = kind?.ProgrammableTransaction;
if (!programmableTx) {
throw new Error('Unable to deserialize from bytes.');
}
const serialized = create(
{
version: 1,
gasConfig: {},
inputs: programmableTx.inputs.map((value: unknown, index: number) =>
create(
{
kind: 'Input',
value,
index,
type: is(value, PureCallArg) ? 'pure' : 'object',
},
TransactionBlockInput
)
),
transactions: programmableTx.transactions,
},
SerializedTransactionDataBuilder
);
return TransactionBlockDataBuilder.restore(serialized);
}
static fromBytes(bytes: Uint8Array) {
const rawData = builder.de('TransactionData', bytes);
const data = rawData?.V1;
const programmableTx = data?.kind?.ProgrammableTransaction;
if (!data || !programmableTx) {
throw new Error('Unable to deserialize from bytes.');
}
const serialized = create(
{
version: 1,
sender: data.sender,
expiration: data.expiration,
gasConfig: data.gasData,
inputs: programmableTx.inputs.map((value: unknown, index: number) =>
create(
{
kind: 'Input',
value,
index,
type: is(value, PureCallArg) ? 'pure' : 'object',
},
TransactionBlockInput
)
),
transactions: programmableTx.transactions,
},
SerializedTransactionDataBuilder
);
return TransactionBlockDataBuilder.restore(serialized);
}
static restore(data: SerializedTransactionDataBuilder) {
assert(data, SerializedTransactionDataBuilder);
const transactionData = new TransactionBlockDataBuilder();
Object.assign(transactionData, data);
return transactionData;
}
/**
* Generate transaction digest.
*
* @param bytes BCS serialized transaction data
* @returns transaction digest.
*/
static getDigestFromBytes(bytes: Uint8Array) {
const hash = hashTypedData('TransactionData', bytes);
return toB58(hash);
}
version = 1 as const;
sender?: string;
expiration?: TransactionExpiration;
gasConfig: GasConfig;
inputs: TransactionBlockInput[];
transactions: TransactionType[];
constructor(clone?: TransactionBlockDataBuilder) {
this.sender = clone?.sender;
this.expiration = clone?.expiration;
this.gasConfig = clone?.gasConfig ?? {};
this.inputs = clone?.inputs ?? [];
this.transactions = clone?.transactions ?? [];
}
build({
overrides,
onlyTransactionKind,
}: {
overrides?: Pick<Partial<TransactionBlockDataBuilder>, 'sender' | 'gasConfig' | 'expiration'>;
onlyTransactionKind?: boolean;
} = {}) {
// Resolve inputs down to values:
const inputs = this.inputs.map((input) => {
assert(input.value, BuilderCallArg);
return input.value;
});
const kind = {
ProgrammableTransaction: {
inputs,
transactions: this.transactions,
},
};
if (onlyTransactionKind) {
return builder.ser('TransactionKind', kind, { maxSize: TRANSACTION_DATA_MAX_SIZE }).toBytes();
}
const expiration = overrides?.expiration ?? this.expiration;
const sender = overrides?.sender ?? this.sender;
const gasConfig = { ...this.gasConfig, ...overrides?.gasConfig };
if (!sender) {
throw new Error('Missing transaction sender');
}
if (!gasConfig.budget) {
throw new Error('Missing gas budget');
}
if (!gasConfig.payment) {
throw new Error('Missing gas payment');
}
if (!gasConfig.price) {
throw new Error('Missing gas price');
}
const transactionData = {
sender: prepareSuiAddress(sender),
expiration: expiration ? expiration : { None: true },
gasData: {
payment: gasConfig.payment,
owner: prepareSuiAddress(this.gasConfig.owner ?? sender),
price: BigInt(gasConfig.price),
budget: BigInt(gasConfig.budget),
},
kind: {
ProgrammableTransaction: {
inputs,
transactions: this.transactions,
},
},
};
return builder.ser('TransactionData', { V1: transactionData }, { maxSize: TRANSACTION_DATA_MAX_SIZE }).toBytes();
}
getDigest() {
const bytes = this.build({ onlyTransactionKind: false });
return TransactionBlockDataBuilder.getDigestFromBytes(bytes);
}
snapshot(): SerializedTransactionDataBuilder {
return create(this, SerializedTransactionDataBuilder);
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!