@mysten/sui
Version:
Sui TypeScript API(Work in Progress)
185 lines (156 loc) • 5.47 kB
text/typescript
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
import { toB58 } from '@mysten/bcs';
import { describe, expect, it } from 'vitest';
import { bcs } from '../../bcs/index.js';
import { Commands, Transaction } from '../index.js';
import { Inputs } from '../Inputs.js';
it('can construct and serialize an empty tranaction', () => {
const tx = new Transaction();
expect(() => tx.serialize()).not.toThrow();
});
it('can construct a receiving transaction argument', () => {
const tx = new Transaction();
tx.object(Inputs.ReceivingRef(ref()));
expect(() => tx.serialize()).not.toThrow();
});
it('receiving transaction argument different from object argument', () => {
const oref = ref();
const rtx = new Transaction();
rtx.object(Inputs.ReceivingRef(oref));
const otx = new Transaction();
otx.object(Inputs.ObjectRef(oref));
expect(() => rtx.serialize()).not.toThrow();
expect(() => otx.serialize()).not.toThrow();
expect(otx.serialize()).not.toEqual(rtx.serialize());
});
it('can be serialized and deserialized to the same values', () => {
const tx = new Transaction();
tx.add(Commands.SplitCoins(tx.gas, [tx.pure.u64(100)]));
const serialized = tx.serialize();
const tx2 = Transaction.from(serialized);
expect(serialized).toEqual(tx2.serialize());
});
it('allows transfer with the result of split Commands', () => {
const tx = new Transaction();
const coin = tx.add(Commands.SplitCoins(tx.gas, [tx.pure.u64(100)]));
tx.add(Commands.TransferObjects([coin], tx.object('0x2')));
});
it('supports nested results through either array index or destructuring', () => {
const tx = new Transaction();
const registerResult = tx.add(
Commands.MoveCall({
target: '0x2::game::register',
}),
);
const [nft, account] = registerResult;
// NOTE: This might seem silly but destructuring works differently than property access.
expect(nft).toBe(registerResult[0]);
expect(account).toBe(registerResult[1]);
});
describe('offline build', () => {
it('builds an empty transaction offline when provided sufficient data', async () => {
const tx = setup();
await tx.build();
});
it('supports epoch expiration', async () => {
const tx = setup();
tx.setExpiration({ Epoch: 1 });
await tx.build();
});
it('builds a split transaction', async () => {
const tx = setup();
tx.add(Commands.SplitCoins(tx.gas, [tx.pure.u64(100)]));
await tx.build();
});
it('breaks reference equality', () => {
const tx = setup();
const tx2 = Transaction.from(tx);
tx.setGasBudget(999);
// Ensure that setting budget after a clone does not affect the original:
expect(tx2.blockData).not.toEqual(tx.blockData);
// Ensure `blockData` always breaks reference equality:
expect(tx.blockData).not.toBe(tx.blockData);
expect(tx.blockData.gasConfig).not.toBe(tx.blockData.gasConfig);
expect(tx.blockData.transactions).not.toBe(tx.blockData.transactions);
expect(tx.blockData.inputs).not.toBe(tx.blockData.inputs);
});
it('can determine the type of inputs for built-in Commands', async () => {
const tx = setup();
tx.splitCoins(tx.gas, [100]);
await tx.build();
});
it('supports pre-serialized inputs as Uint8Array', async () => {
const tx = setup();
const inputBytes = bcs.U64.serialize(100n).toBytes();
// Use bytes directly in pure value:
tx.add(Commands.SplitCoins(tx.gas, [tx.pure(inputBytes)]));
await tx.build();
});
it('builds a more complex interaction', async () => {
const tx = setup();
const coin = tx.splitCoins(tx.gas, [100]);
tx.add(Commands.MergeCoins(tx.gas, [coin, tx.object(Inputs.ObjectRef(ref()))]));
tx.add(
Commands.MoveCall({
target: '0x2::devnet_nft::mint',
typeArguments: [],
arguments: [tx.pure.string('foo'), tx.pure.string('bar'), tx.pure.string('baz')],
}),
);
await tx.build();
});
it('uses a receiving argument', async () => {
const tx = setup();
tx.object(Inputs.ObjectRef(ref()));
const coin = tx.splitCoins(tx.gas, [100]);
tx.add(Commands.MergeCoins(tx.gas, [coin, tx.object(Inputs.ObjectRef(ref()))]));
tx.add(
Commands.MoveCall({
target: '0x2::devnet_nft::mint',
typeArguments: [],
arguments: [tx.object(Inputs.ObjectRef(ref())), tx.object(Inputs.ReceivingRef(ref()))],
}),
);
const bytes = await tx.build();
const tx2 = Transaction.from(bytes);
const bytes2 = await tx2.build();
expect(bytes).toEqual(bytes2);
});
it('builds a more complex interaction', async () => {
const tx = setup();
const coin = tx.splitCoins(tx.gas, [100]);
tx.add(Commands.MergeCoins(tx.gas, [coin, tx.object(Inputs.ObjectRef(ref()))]));
tx.add(
Commands.MoveCall({
target: '0x2::devnet_nft::mint',
typeArguments: [],
arguments: [tx.pure.string('foo'), tx.pure.string('bar'), tx.pure.string('baz')],
}),
);
const bytes = await tx.build();
const tx2 = Transaction.from(bytes);
const bytes2 = await tx2.build();
expect(bytes).toEqual(bytes2);
});
});
function ref(): { objectId: string; version: string; digest: string } {
return {
objectId: (Math.random() * 100000).toFixed(0).padEnd(64, '0'),
version: String((Math.random() * 10000).toFixed(0)),
digest: toB58(
new Uint8Array([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1,
2,
]),
),
};
}
function setup() {
const tx = new Transaction();
tx.setSender('0x2');
tx.setGasPrice(5);
tx.setGasBudget(100);
tx.setGasPayment([ref()]);
return tx;
}