PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@iota/iota-sdk/src/transactions/data

Просмотр файла: v1.ts

// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { fromBase64, toBase64 } from '@iota/bcs';
import type { GenericSchema, InferInput, InferOutput } from 'valibot';
import {
    array,
    bigint,
    boolean,
    check,
    integer,
    is,
    lazy,
    literal,
    nullable,
    nullish,
    number,
    object,
    optional,
    parse,
    pipe,
    string,
    union,
    unknown,
} from 'valibot';

import { TypeTagSerializer } from '../../bcs/index.js';
import type { StructTag as StructTagType, TypeTag as TypeTagType } from '../../bcs/types.js';
import { JsonU64, ObjectID, safeEnum, TransactionData } from './internal.js';
import type { Argument } from './internal.js';

export const ObjectRef = object({
    digest: string(),
    objectId: string(),
    version: union([pipe(number(), integer()), string(), bigint()]),
});

const ObjectArg = safeEnum({
    ImmOrOwned: ObjectRef,
    Shared: object({
        objectId: ObjectID,
        initialSharedVersion: JsonU64,
        mutable: boolean(),
    }),
    Receiving: ObjectRef,
});

export const NormalizedCallArg = safeEnum({
    Object: ObjectArg,
    Pure: array(pipe(number(), integer())),
});

const TransactionInput = union([
    object({
        kind: literal('Input'),
        index: pipe(number(), integer()),
        value: unknown(),
        type: optional(literal('object')),
    }),
    object({
        kind: literal('Input'),
        index: pipe(number(), integer()),
        value: unknown(),
        type: literal('pure'),
    }),
]);

const TransactionExpiration = union([
    object({ Epoch: pipe(number(), integer()) }),
    object({ None: nullable(literal(true)) }),
]);

const StringEncodedBigint = pipe(
    union([number(), string(), bigint()]),
    check((val) => {
        if (!['string', 'number', 'bigint'].includes(typeof val)) return false;

        try {
            BigInt(val as string);
            return true;
        } catch {
            return false;
        }
    }),
);

export const TypeTag: GenericSchema<TypeTagType> = union([
    object({ bool: nullable(literal(true)) }),
    object({ u8: nullable(literal(true)) }),
    object({ u64: nullable(literal(true)) }),
    object({ u128: nullable(literal(true)) }),
    object({ address: nullable(literal(true)) }),
    object({ signer: nullable(literal(true)) }),
    object({ vector: lazy(() => TypeTag) }),
    object({ struct: lazy(() => StructTag) }),
    object({ u16: nullable(literal(true)) }),
    object({ u32: nullable(literal(true)) }),
    object({ u256: nullable(literal(true)) }),
]);

// https://github.com/iotaledger/iota/blob/cea8742e810142a8145fd83c4c142d61e561004a/external-crates/move/crates/move-core-types/src/language_storage.rs#L140-L147
export const StructTag: GenericSchema<StructTagType> = object({
    address: string(),
    module: string(),
    name: string(),
    typeParams: array(TypeTag),
});

const GasConfig = object({
    budget: optional(StringEncodedBigint),
    price: optional(StringEncodedBigint),
    payment: optional(array(ObjectRef)),
    owner: optional(string()),
});

const TransactionArgumentTypes = [
    TransactionInput,
    object({ kind: literal('GasCoin') }),
    object({ kind: literal('Result'), index: pipe(number(), integer()) }),
    object({
        kind: literal('NestedResult'),
        index: pipe(number(), integer()),
        resultIndex: pipe(number(), integer()),
    }),
] as const;

// Generic transaction argument
export const TransactionArgument = union([...TransactionArgumentTypes]);

const MoveCallTransaction = object({
    kind: literal('MoveCall'),
    target: pipe(
        string(),
        check((target) => target.split('::').length === 3),
    ) as GenericSchema<`${string}::${string}::${string}`>,
    typeArguments: array(string()),
    arguments: array(TransactionArgument),
});

const TransferObjectsTransaction = object({
    kind: literal('TransferObjects'),
    objects: array(TransactionArgument),
    address: TransactionArgument,
});

const SplitCoinsTransaction = object({
    kind: literal('SplitCoins'),
    coin: TransactionArgument,
    amounts: array(TransactionArgument),
});

const MergeCoinsTransaction = object({
    kind: literal('MergeCoins'),
    destination: TransactionArgument,
    sources: array(TransactionArgument),
});

const MakeMoveVecTransaction = object({
    kind: literal('MakeMoveVec'),
    type: union([object({ Some: TypeTag }), object({ None: nullable(literal(true)) })]),
    objects: array(TransactionArgument),
});

const PublishTransaction = object({
    kind: literal('Publish'),
    modules: array(array(pipe(number(), integer()))),
    dependencies: array(string()),
});

const UpgradeTransaction = object({
    kind: literal('Upgrade'),
    modules: array(array(pipe(number(), integer()))),
    dependencies: array(string()),
    packageId: string(),
    ticket: TransactionArgument,
});

const TransactionTypes = [
    MoveCallTransaction,
    TransferObjectsTransaction,
    SplitCoinsTransaction,
    MergeCoinsTransaction,
    PublishTransaction,
    UpgradeTransaction,
    MakeMoveVecTransaction,
] as const;

const TransactionType = union([...TransactionTypes]);

export const SerializedTransactionDataV1 = object({
    version: literal(1),
    sender: optional(string()),
    expiration: nullish(TransactionExpiration),
    gasConfig: GasConfig,
    inputs: array(TransactionInput),
    transactions: array(TransactionType),
});

export type SerializedTransactionDataV1 = InferOutput<typeof SerializedTransactionDataV1>;

export function serializeV1TransactionData(
    transactionData: TransactionData,
): SerializedTransactionDataV1 {
    const inputs: InferOutput<typeof TransactionInput>[] = transactionData.inputs.map(
        (input, index) => {
            if (input.Object) {
                return {
                    kind: 'Input',
                    index,
                    value: {
                        Object: input.Object.ImmOrOwnedObject
                            ? {
                                  ImmOrOwned: input.Object.ImmOrOwnedObject,
                              }
                            : input.Object.Receiving
                              ? {
                                    Receiving: {
                                        digest: input.Object.Receiving.digest,
                                        version: input.Object.Receiving.version,
                                        objectId: input.Object.Receiving.objectId,
                                    },
                                }
                              : {
                                    Shared: {
                                        mutable: input.Object.SharedObject.mutable,
                                        initialSharedVersion:
                                            input.Object.SharedObject.initialSharedVersion,
                                        objectId: input.Object.SharedObject.objectId,
                                    },
                                },
                    },
                    type: 'object',
                };
            }
            if (input.Pure) {
                return {
                    kind: 'Input',
                    index,
                    value: {
                        Pure: Array.from(fromBase64(input.Pure.bytes)),
                    },
                    type: 'pure',
                };
            }

            if (input.UnresolvedPure) {
                return {
                    kind: 'Input',
                    type: 'pure',
                    index,
                    value: input.UnresolvedPure.value,
                };
            }

            if (input.UnresolvedObject) {
                return {
                    kind: 'Input',
                    type: 'object',
                    index,
                    value: input.UnresolvedObject.objectId,
                };
            }

            throw new Error('Invalid input');
        },
    );

    return {
        version: 1,
        sender: transactionData.sender ?? undefined,
        expiration:
            transactionData.expiration?.$kind === 'Epoch'
                ? { Epoch: Number(transactionData.expiration.Epoch) }
                : transactionData.expiration
                  ? { None: true }
                  : null,
        gasConfig: {
            owner: transactionData.gasData.owner ?? undefined,
            budget: transactionData.gasData.budget ?? undefined,
            price: transactionData.gasData.price ?? undefined,
            payment: transactionData.gasData.payment ?? undefined,
        },
        inputs,
        transactions: transactionData.commands.map(
            (command): InferOutput<typeof TransactionType> => {
                if (command.MakeMoveVec) {
                    return {
                        kind: 'MakeMoveVec',
                        type:
                            command.MakeMoveVec.type === null
                                ? { None: true }
                                : {
                                      Some: TypeTagSerializer.parseFromStr(
                                          command.MakeMoveVec.type,
                                      ),
                                  },
                        objects: command.MakeMoveVec.elements.map((arg) =>
                            convertTransactionArgument(arg, inputs),
                        ),
                    };
                }
                if (command.MergeCoins) {
                    return {
                        kind: 'MergeCoins',
                        destination: convertTransactionArgument(
                            command.MergeCoins.destination,
                            inputs,
                        ),
                        sources: command.MergeCoins.sources.map((arg) =>
                            convertTransactionArgument(arg, inputs),
                        ),
                    };
                }
                if (command.MoveCall) {
                    return {
                        kind: 'MoveCall',
                        target: `${command.MoveCall.package}::${command.MoveCall.module}::${command.MoveCall.function}`,
                        typeArguments: command.MoveCall.typeArguments,
                        arguments: command.MoveCall.arguments.map((arg) =>
                            convertTransactionArgument(arg, inputs),
                        ),
                    };
                }
                if (command.Publish) {
                    return {
                        kind: 'Publish',
                        modules: command.Publish.modules.map((mod) => Array.from(fromBase64(mod))),
                        dependencies: command.Publish.dependencies,
                    };
                }
                if (command.SplitCoins) {
                    return {
                        kind: 'SplitCoins',
                        coin: convertTransactionArgument(command.SplitCoins.coin, inputs),
                        amounts: command.SplitCoins.amounts.map((arg) =>
                            convertTransactionArgument(arg, inputs),
                        ),
                    };
                }
                if (command.TransferObjects) {
                    return {
                        kind: 'TransferObjects',
                        objects: command.TransferObjects.objects.map((arg) =>
                            convertTransactionArgument(arg, inputs),
                        ),
                        address: convertTransactionArgument(
                            command.TransferObjects.address,
                            inputs,
                        ),
                    };
                }

                if (command.Upgrade) {
                    return {
                        kind: 'Upgrade',
                        modules: command.Upgrade.modules.map((mod) => Array.from(fromBase64(mod))),
                        dependencies: command.Upgrade.dependencies,
                        packageId: command.Upgrade.package,
                        ticket: convertTransactionArgument(command.Upgrade.ticket, inputs),
                    };
                }

                throw new Error(`Unknown transaction ${Object.keys(command)}`);
            },
        ),
    };
}

function convertTransactionArgument(
    arg: Argument,
    inputs: InferOutput<typeof TransactionInput>[],
): InferOutput<typeof TransactionArgument> {
    if (arg.$kind === 'GasCoin') {
        return { kind: 'GasCoin' };
    }
    if (arg.$kind === 'Result') {
        return { kind: 'Result', index: arg.Result };
    }
    if (arg.$kind === 'NestedResult') {
        return {
            kind: 'NestedResult',
            index: arg.NestedResult[0],
            resultIndex: arg.NestedResult[1],
        };
    }
    if (arg.$kind === 'Input') {
        return inputs[arg.Input];
    }

    throw new Error(`Invalid argument ${Object.keys(arg)}`);
}

export function transactionDataFromV1(data: SerializedTransactionDataV1): TransactionData {
    return parse(TransactionData, {
        version: 2,
        sender: data.sender ?? null,
        expiration: data.expiration
            ? 'Epoch' in data.expiration
                ? { Epoch: data.expiration.Epoch }
                : { None: true }
            : null,
        gasData: {
            owner: data.gasConfig.owner ?? null,
            budget: data.gasConfig.budget?.toString() ?? null,
            price: data.gasConfig.price?.toString() ?? null,
            payment:
                data.gasConfig.payment?.map((ref) => ({
                    digest: ref.digest,
                    objectId: ref.objectId,
                    version: ref.version.toString(),
                })) ?? null,
        },
        inputs: data.inputs.map((input) => {
            if (input.kind === 'Input') {
                if (is(NormalizedCallArg, input.value)) {
                    const value = parse(NormalizedCallArg, input.value);

                    if (value.Object) {
                        if (value.Object.ImmOrOwned) {
                            return {
                                Object: {
                                    ImmOrOwnedObject: {
                                        objectId: value.Object.ImmOrOwned.objectId,
                                        version: String(value.Object.ImmOrOwned.version),
                                        digest: value.Object.ImmOrOwned.digest,
                                    },
                                },
                            };
                        }
                        if (value.Object.Shared) {
                            return {
                                Object: {
                                    SharedObject: {
                                        mutable: value.Object.Shared.mutable ?? null,
                                        initialSharedVersion:
                                            value.Object.Shared.initialSharedVersion,
                                        objectId: value.Object.Shared.objectId,
                                    },
                                },
                            };
                        }
                        if (value.Object.Receiving) {
                            return {
                                Object: {
                                    Receiving: {
                                        digest: value.Object.Receiving.digest,
                                        version: String(value.Object.Receiving.version),
                                        objectId: value.Object.Receiving.objectId,
                                    },
                                },
                            };
                        }

                        throw new Error('Invalid object input');
                    }

                    return {
                        Pure: {
                            bytes: toBase64(new Uint8Array(value.Pure)),
                        },
                    };
                }

                if (input.type === 'object') {
                    return {
                        UnresolvedObject: {
                            objectId: input.value as string,
                        },
                    };
                }

                return {
                    UnresolvedPure: {
                        value: input.value,
                    },
                };
            }

            throw new Error('Invalid input');
        }),
        commands: data.transactions.map((transaction) => {
            switch (transaction.kind) {
                case 'MakeMoveVec':
                    return {
                        MakeMoveVec: {
                            type:
                                'Some' in transaction.type
                                    ? TypeTagSerializer.tagToString(transaction.type.Some)
                                    : null,
                            elements: transaction.objects.map((arg) =>
                                parseV1TransactionArgument(arg),
                            ),
                        },
                    };
                case 'MergeCoins': {
                    return {
                        MergeCoins: {
                            destination: parseV1TransactionArgument(transaction.destination),
                            sources: transaction.sources.map((arg) =>
                                parseV1TransactionArgument(arg),
                            ),
                        },
                    };
                }
                case 'MoveCall': {
                    const [pkg, mod, fn] = transaction.target.split('::');
                    return {
                        MoveCall: {
                            package: pkg,
                            module: mod,
                            function: fn,
                            typeArguments: transaction.typeArguments,
                            arguments: transaction.arguments.map((arg) =>
                                parseV1TransactionArgument(arg),
                            ),
                        },
                    };
                }
                case 'Publish': {
                    return {
                        Publish: {
                            modules: transaction.modules.map((mod) =>
                                toBase64(Uint8Array.from(mod)),
                            ),
                            dependencies: transaction.dependencies,
                        },
                    };
                }
                case 'SplitCoins': {
                    return {
                        SplitCoins: {
                            coin: parseV1TransactionArgument(transaction.coin),
                            amounts: transaction.amounts.map((arg) =>
                                parseV1TransactionArgument(arg),
                            ),
                        },
                    };
                }
                case 'TransferObjects': {
                    return {
                        TransferObjects: {
                            objects: transaction.objects.map((arg) =>
                                parseV1TransactionArgument(arg),
                            ),
                            address: parseV1TransactionArgument(transaction.address),
                        },
                    };
                }
                case 'Upgrade': {
                    return {
                        Upgrade: {
                            modules: transaction.modules.map((mod) =>
                                toBase64(Uint8Array.from(mod)),
                            ),
                            dependencies: transaction.dependencies,
                            package: transaction.packageId,
                            ticket: parseV1TransactionArgument(transaction.ticket),
                        },
                    };
                }
            }

            throw new Error(`Unknown transaction ${Object.keys(transaction)}`);
        }),
    } satisfies InferInput<typeof TransactionData>);
}

function parseV1TransactionArgument(
    arg: InferOutput<typeof TransactionArgument>,
): InferInput<typeof Argument> {
    switch (arg.kind) {
        case 'GasCoin': {
            return { GasCoin: true };
        }
        case 'Result':
            return { Result: arg.index };
        case 'NestedResult': {
            return { NestedResult: [arg.index, arg.resultIndex] };
        }
        case 'Input': {
            return { Input: arg.index };
        }
    }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!