From 61d7b686b9088c3f7645be2d445fe26cfd83237e Mon Sep 17 00:00:00 2001 From: Michael Hayes Date: Wed, 6 Nov 2024 12:53:56 -0800 Subject: [PATCH] [ts-sdk] Add tx.object.option for creatnig object options in transaction builder --- .changeset/late-dancers-rush.md | 5 + .../transaction-building/basics.mdx | 20 ++- sdk/typescript/package.json | 2 +- sdk/typescript/src/transactions/object.ts | 10 +- .../test/e2e/data/serializer/Move.lock | 12 +- .../data/serializer/sources/serializer.move | 115 ++++++++++-------- sdk/typescript/test/e2e/txn-builder.test.ts | 41 +++++++ sdk/typescript/test/e2e/utils/setup.ts | 3 + sdk/typescript/test/unit/arguments.test.ts | 78 ++++++++++++ .../test/unit/object-inputs.test.ts | 78 ++++++++++++ 10 files changed, 305 insertions(+), 59 deletions(-) create mode 100644 .changeset/late-dancers-rush.md diff --git a/.changeset/late-dancers-rush.md b/.changeset/late-dancers-rush.md new file mode 100644 index 0000000000000..ffaef2ac02677 --- /dev/null +++ b/.changeset/late-dancers-rush.md @@ -0,0 +1,5 @@ +--- +'@mysten/sui': patch +--- + +Add tx.object.option for creatnig object options in transaction builder diff --git a/sdk/docs/pages/typescript/transaction-building/basics.mdx b/sdk/docs/pages/typescript/transaction-building/basics.mdx index 12b0f8d767e4d..a0ce0099232ea 100644 --- a/sdk/docs/pages/typescript/transaction-building/basics.mdx +++ b/sdk/docs/pages/typescript/transaction-building/basics.mdx @@ -190,7 +190,7 @@ tx.moveCall({ arguments: [tx.object('0xSomeObject')], }); -// tx.object automaically converts the object ID to receiving transaction arguments if the moveCall expects it +// tx.object automatically converts the object ID to receiving transaction arguments if the moveCall expects it tx.moveCall({ target: '0xSomeAddress::example::receive_object', // 0xSomeAddress::example::receive_object expects a receiving argument and has a Move definition that looks like this: @@ -217,6 +217,24 @@ tx.object(Inputs.SharedObjectRef({ objectId, initialSharedVersion, mutable })); tx.object(Inputs.ReceivingRef({ digest, objectId, version })); ``` +##### Object helpers + +There are a handful of specific object types that can be referenced through helper methods on +tx.object: + +```ts +tx.object.system(), +tx.object.clock(), +tx.object.random(), +tx.object.denyList(), + +tx.object.option({ + type: '0x123::example::Thing', + // value can be an Object ID, or any other object reference, or null for `none` + value: '0x456', +}), +``` + #### Transaction results You can also use the result of a transaction as an argument in a subsequent transactions. Each diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 760b955abb3cb..5d3daf664f397 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -102,7 +102,7 @@ "test:unit": "vitest run unit __tests__", "test:e2e": "wait-on http://127.0.0.1:9123 -l --timeout 180000 && vitest run e2e", "test:e2e:nowait": "vitest run e2e", - "prepare:e2e": "docker-compose down && docker-compose up -d && cargo build --bin sui --profile dev && cross-env RUST_LOG=warn,sui=error,anemo_tower=warn,consensus=off ../../target/debug/sui start --with-faucet --force-regenesis --with-indexer --pg-port 5435 --pg-db-name sui_indexer_v2 --with-graphql", + "prepare:e2e": "docker-compose down && docker-compose up -d && cargo build --bin sui --profile dev && cross-env RUST_LOG=warn,anemo_tower=warn,consensus=off ../../target/debug/sui start --with-faucet --force-regenesis --with-indexer --pg-port 5435 --pg-db-name sui_indexer_v2 --with-graphql", "prepublishOnly": "pnpm build", "size": "size-limit", "analyze": "size-limit --why", diff --git a/sdk/typescript/src/transactions/object.ts b/sdk/typescript/src/transactions/object.ts index ff83fac9dc750..e17fed5f1de21 100644 --- a/sdk/typescript/src/transactions/object.ts +++ b/sdk/typescript/src/transactions/object.ts @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -import type { TransactionObjectInput } from './Transaction.js'; +import type { Transaction, TransactionObjectInput } from './Transaction.js'; export function createObjectMethods(makeObject: (value: TransactionObjectInput) => T) { function object(value: TransactionObjectInput) { @@ -12,6 +12,14 @@ export function createObjectMethods(makeObject: (value: TransactionObjectInpu object.clock = () => object('0x6'); object.random = () => object('0x8'); object.denyList = () => object('0x403'); + object.option = + ({ type, value }: { type: string; value: TransactionObjectInput | null }) => + (tx: Transaction) => + tx.moveCall({ + typeArguments: [type], + target: `0x1::option::${value === null ? 'none' : 'some'}`, + arguments: value === null ? [] : [tx.object(value)], + }); return object; } diff --git a/sdk/typescript/test/e2e/data/serializer/Move.lock b/sdk/typescript/test/e2e/data/serializer/Move.lock index 0da12b5b3f133..b423d605853fd 100644 --- a/sdk/typescript/test/e2e/data/serializer/Move.lock +++ b/sdk/typescript/test/e2e/data/serializer/Move.lock @@ -1,27 +1,27 @@ # @generated by Move, please check-in and do not edit manually. [move] -version = 0 +version = 3 manifest_digest = "8C8B9ADAFF8B7267E4476A3DF08A72810194D36146AC710F0545C8843B1F1075" deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" dependencies = [ - { name = "Sui" }, + { id = "Sui", name = "Sui" }, ] [[move.package]] -name = "MoveStdlib" +id = "MoveStdlib" source = { local = "../../../../../../crates/sui-framework/packages/move-stdlib" } [[move.package]] -name = "Sui" +id = "Sui" source = { local = "../../../../../../crates/sui-framework/packages/sui-framework" } dependencies = [ - { name = "MoveStdlib" }, + { id = "MoveStdlib", name = "MoveStdlib" }, ] [move.toolchain-version] -compiler-version = "1.30.0" +compiler-version = "1.38.0" edition = "2024.beta" flavor = "sui" diff --git a/sdk/typescript/test/e2e/data/serializer/sources/serializer.move b/sdk/typescript/test/e2e/data/serializer/sources/serializer.move index d990ce1ac5bef..00152435e58e7 100644 --- a/sdk/typescript/test/e2e/data/serializer/sources/serializer.move +++ b/sdk/typescript/test/e2e/data/serializer/sources/serializer.move @@ -1,66 +1,81 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module serializer::serializer_tests { - use sui::clock::Clock; - use std::option::Option; - use sui::object::ID; - use std::string::String; - use std::ascii; - - public struct MutableShared has key { - id: UID, - value: u64, - } +module serializer::serializer_tests; - fun init(ctx: &mut TxContext) { - transfer::share_object(MutableShared { - id: object::new(ctx), - value: 1, - }) - } +use std::ascii; +use std::option::{is_some, extract}; +use std::string::String; +use sui::clock::Clock; - public entry fun use_clock(_clock: &Clock) {} +public struct MutableShared has key { + id: UID, + value: u64, +} - public entry fun list( - item: T, - ctx: &mut TxContext - ) { - transfer::public_transfer(item, tx_context::sender(ctx)) - } +fun init(ctx: &mut TxContext) { + transfer::share_object(MutableShared { + id: object::new(ctx), + value: 1, + }) +} - public fun return_struct( - item: T, - ): T { - item - } +public entry fun use_clock(_clock: &Clock) {} - public entry fun value(clock: &MutableShared) { - assert!(clock.value > 0, 1); - } +public entry fun list(item: T, ctx: &mut TxContext) { + transfer::public_transfer(item, tx_context::sender(ctx)) +} - public entry fun set_value(clock: &mut MutableShared) { - clock.value = 10; - } +public fun return_struct(item: T): T { + item +} - public entry fun delete_value(clock: MutableShared) { - let MutableShared { id, value: _ } = clock; - object::delete(id); - } +public entry fun value(clock: &MutableShared) { + assert!(clock.value > 0, 1); +} - public fun test_abort() { - abort 0 - } +public entry fun set_value(clock: &mut MutableShared) { + clock.value = 10; +} + +public entry fun delete_value(clock: MutableShared) { + let MutableShared { id, value: _ } = clock; + object::delete(id); +} + +public fun test_abort() { + abort 0 +} + +public fun addr(_: address) {} + +public fun id(_: ID) {} - public fun addr(_: address) {} - public fun id(_: ID) {} +public fun ascii_(_: ascii::String) {} - public fun ascii_(_: ascii::String) {} - public fun string(_: String) {} +public fun string(_: String) {} - public fun vec(_: vector) {} - public fun opt(_: Option) {} +public fun vec(_: vector) {} - public fun ints(_u8: u8, _u16: u16, _u32: u32, _u64: u64, _u128: u128, _u256: u256) {} - public fun boolean(_bool: bool) {} +public fun opt(_: Option) {} + +public fun ints( + _u8: u8, + _u16: u16, + _u32: u32, + _u64: u64, + _u128: u128, + _u256: u256, +) {} + +public fun boolean(_bool: bool) {} + +public fun some(opt: &mut Option): T { + extract(opt) +} + +public fun none(opt: &mut Option) { + if (is_some(opt)) { + abort 1 + } } diff --git a/sdk/typescript/test/e2e/txn-builder.test.ts b/sdk/typescript/test/e2e/txn-builder.test.ts index 75486345488be..a4053fea89816 100644 --- a/sdk/typescript/test/e2e/txn-builder.test.ts +++ b/sdk/typescript/test/e2e/txn-builder.test.ts @@ -208,6 +208,47 @@ describe('Transaction Builders', () => { retry: 10, }, ); + + it('builds object options', async () => { + const tx = new Transaction(); + + tx.moveCall({ + target: `${packageId}::serializer_tests::none`, + typeArguments: ['0x2::coin::Coin<0x2::sui::SUI>'], + arguments: [ + tx.object.option({ + type: '0x2::coin::Coin<0x2::sui::SUI>', + value: null, + }), + ], + }); + const coin = tx.splitCoins(tx.gas, [1]) + const coin2 = tx.moveCall({ + target: `${packageId}::serializer_tests::some`, + typeArguments: ['0x2::coin::Coin<0x2::sui::SUI>'], + arguments: [ + tx.object.option({ + type: '0x2::coin::Coin<0x2::sui::SUI>', + value: coin, + }), + ], + }); + + const coin3 = tx.moveCall({ + target: `${packageId}::serializer_tests::some`, + typeArguments: ['0x2::coin::Coin<0x2::sui::SUI>'], + arguments: [ + tx.object.option({ + type: '0x2::coin::Coin<0x2::sui::SUI>', + value: coin2, + }), + ], + }); + + tx.transferObjects([coin3], toolbox.keypair.toSuiAddress()); + + await validateTransaction(toolbox.client, toolbox.keypair, tx); + }); }); async function validateTransaction(client: SuiClient, signer: Keypair, tx: Transaction) { diff --git a/sdk/typescript/test/e2e/utils/setup.ts b/sdk/typescript/test/e2e/utils/setup.ts index 63362790eee31..4562dce64ba15 100644 --- a/sdk/typescript/test/e2e/utils/setup.ts +++ b/sdk/typescript/test/e2e/utils/setup.ts @@ -172,12 +172,15 @@ export async function publishPackage(packagePath: string, toolbox?: TestToolbox) const tmpobj = tmp.dirSync({ unsafeCleanup: true }); + console.log('building package'); const { modules, dependencies } = JSON.parse( execSync( `${SUI_BIN} move --client.config ${toolbox.configPath} build --dump-bytecode-as-base64 --path ${packagePath} --install-dir ${tmpobj.name}`, { encoding: 'utf-8' }, ), ); + + console.log('publishing'); const tx = new Transaction(); const cap = tx.publish({ modules, diff --git a/sdk/typescript/test/unit/arguments.test.ts b/sdk/typescript/test/unit/arguments.test.ts index 2748d8f5b626d..6d7a7e2bdfd0b 100644 --- a/sdk/typescript/test/unit/arguments.test.ts +++ b/sdk/typescript/test/unit/arguments.test.ts @@ -30,6 +30,18 @@ describe('Arguments helpers', () => { Arguments.object.clock(), Arguments.object.random(), Arguments.object.denyList(), + Arguments.object.option({ + type: '0x123::example::Thing', + value: '0x456', + }), + Arguments.object.option({ + type: '0x123::example::Thing', + value: Arguments.object('0x456'), + }), + Arguments.object.option({ + type: '0x123::example::Thing', + value: null, + }), ]; const tx = new Transaction(); @@ -42,6 +54,54 @@ describe('Arguments helpers', () => { expect(tx.getData()).toMatchInlineSnapshot(` { "commands": [ + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [ + { + "$kind": "Input", + "Input": 9, + "type": "object", + }, + ], + "function": "some", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [ + { + "$kind": "Input", + "Input": 9, + "type": "object", + }, + ], + "function": "some", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [], + "function": "none", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, { "$kind": "MoveCall", "MoveCall": { @@ -91,6 +151,18 @@ describe('Arguments helpers', () => { "Input": 8, "type": "object", }, + { + "$kind": "Result", + "Result": 0, + }, + { + "$kind": "Result", + "Result": 1, + }, + { + "$kind": "Result", + "Result": 2, + }, ], "function": "bar", "module": "foo", @@ -176,6 +248,12 @@ describe('Arguments helpers', () => { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", }, }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000456", + }, + }, ], "sender": null, "version": 2, diff --git a/sdk/typescript/test/unit/object-inputs.test.ts b/sdk/typescript/test/unit/object-inputs.test.ts index 25d834c6065ad..25db4ad3de175 100644 --- a/sdk/typescript/test/unit/object-inputs.test.ts +++ b/sdk/typescript/test/unit/object-inputs.test.ts @@ -34,12 +34,72 @@ describe('Transaction inputs', () => { tx.object.clock(), tx.object.random(), tx.object.denyList(), + tx.object.option({ + type: '0x123::example::Thing', + value: '0x456', + }), + tx.object.option({ + type: '0x123::example::Thing', + value: tx.object('0x456'), + }), + tx.object.option({ + type: '0x123::example::Thing', + value: null, + }), ], }); expect(tx.getData()).toMatchInlineSnapshot(` { "commands": [ + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [ + { + "$kind": "Input", + "Input": 9, + "type": "object", + }, + ], + "function": "some", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [ + { + "$kind": "Input", + "Input": 9, + "type": "object", + }, + ], + "function": "some", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, + { + "$kind": "MoveCall", + "MoveCall": { + "arguments": [], + "function": "none", + "module": "option", + "package": "0x0000000000000000000000000000000000000000000000000000000000000001", + "typeArguments": [ + "0x123::example::Thing", + ], + }, + }, { "$kind": "MoveCall", "MoveCall": { @@ -89,6 +149,18 @@ describe('Transaction inputs', () => { "Input": 8, "type": "object", }, + { + "$kind": "Result", + "Result": 0, + }, + { + "$kind": "Result", + "Result": 1, + }, + { + "$kind": "Result", + "Result": 2, + }, ], "function": "bar", "module": "foo", @@ -174,6 +246,12 @@ describe('Transaction inputs', () => { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", }, }, + { + "$kind": "UnresolvedObject", + "UnresolvedObject": { + "objectId": "0x0000000000000000000000000000000000000000000000000000000000000456", + }, + }, ], "sender": null, "version": 2,