sentioxyz/typemove

Manually setting ‘kind’ property required for transaction args

Closed this issue · 10 comments

Hi there, I am facing a problem passing address types in my moveCalls through the typemove builder.
I generated the types for my package successfully, and I am trying to make a transaction for the following Move function.

Here is the move function signature on Sui:

public fun authorize_api(
    self: &mut SharedObjectA,
    pub: &Publisher,
    addr: address,
    _ctx: &mut TxContext,
) {}

and here is the generated type from typemove:

export function authorizeApi(
      tx: Transaction,
      args: [
        string | TransactionObjectArgument | TransactionArgument,
        string | TransactionObjectArgument | TransactionArgument,
        string | TransactionArgument,
      ],
    ): TransactionArgument &
      [TransactionArgument, TransactionArgument, TransactionArgument] {
      const _args: any[] = [];
      _args.push(transactionArgumentOrObject(args[0], tx));
      _args.push(transactionArgumentOrObject(args[1], tx));
      _args.push(transactionArgumentOrPure(args[2], tx));

      // @ts-ignore
      return tx.moveCall({
        target:
          "PACKAGE_ID::MODULE::authorize_api",
        arguments: _args,
      });
    }

I constantly get InvalidBCSBytes when executing the transaction through the typemove builder.

When I inspect the failed transaction on explorer, I see that Input 2 (which is the addr field) has type "pure", Value type "address", but the Value is 66,48,120,56,99,57,52,97,97,1...1,97,56,99,98,56 (66 bytes) instead of my actual address 0x2313..34234.

My TS code looks like this when I construct the moveCall through the builder

export const authorizeApi = async (
  client: SuiClient,
  shared_object_id: string,
  publisher_id: string,
  targetAddress: string
) => {
  const tx = new Transaction();

  module.builder.authorizeApi(tx, [
    shared_object_id,
    publisher_id,
    targetAddress,
  ]);

   await client.signAndExecuteTransaction({...});
}

I tried passing targetAddress as such as well:

tx.pure.address(targetAddress)

_0x2.address.builder.fromAsciiBytes(tx, [
    targetAddress,
]);

But had no luck. When I execute the moveCall by the Sui SDK directly, everything is ok. The issue is this weird conversion of address to bytes - or maybe something else that I am failing to understand, that is causing the error: 'CommandArgumentError { arg_idx: 2, kind: InvalidBCSBytes } in command 0'

Here is a list of the versions I am using

"dependencies": {
    "@mysten/sui": "^1.14.0",
    "@typemove/move": "^1.6.6",
    "@typemove/sui": "^1.6.6",
    "@types/node": "^22.8.6",
    "dotenv": "^16.4.5",
    "env-cmd": "^10.1.0",
    "ts-node": "^10.9.2",
    "tslib": "^2.8.1",
    "typescript": "^5.6.3"
  }

would you also share the link in the explorer?

Here is an example on testnet.

I found a workaround for this, by defining the Kind of the arg explicitly, and I had to use it for multiple args, and, of different types.

Here are some examples of my code and the places I had to use this hotfix, maybe it will help you fix it 🙏🏻

let addr = tx.pure.address(targetAddress);
addr["kind"] = "Input";

module.builder.authorizeApi(tx, [
    tx.object(shared_object_id),
    tx.object(publisher_id),
    addr,
]);
const [custom_coin] = tx.splitCoins(tx.object(coin_id), [amount]);
custom_coin["kind"] = "Input";

module.builder.deposit(
    tx,
    [tx.object(safe_id), custom_coin, tx.object(publisher_id)],
    [CUSTOM_COIN_TYPE]
);
// Specify input types for the transaction (typemove specific fix)
let userAddressArg = tx.pure.address(userAddress);
userAddressArg["kind"] = "Input";

let tokenValue = tx.pure.u64(BigInt(value));
tokenValue["kind"] = "Input";

let [config] = module.builder.initSend(tx, [registryId]);
config["kind"] = "Input";

module.builder.sendToken(
    tx,
    [
        tx.object(safeId),
        tokenValue,
        userId,
        userAddressArg,
        tx.object(registryId),
        config,
    ],
    [CUSTOM_COIN_TYPE]
);

let addr = tx.pure.address(targetAddress);
addr["kind"] = "Input";

module.builder.authorizeApi(tx, [
    tx.object(shared_object_id),
    tx.object(publisher_id),
    addr,
]);

cut an new release v1.6.8-rc.1

but I have a question in your previous code?
do you really need, the error tx is when you directly pass pure string right?
addr["kind"] = "Input"; after you do tx.pure.address(targetAddress);

It would throw that error even if I passed targetAddress directly in the params

How should I pass the args correctly with the new release? Because I updated to 1.6.8 and running this gives me SyntaxError: Cannot convert [object Object] to a BigInt

module.builder.authorizeApi(tx, [
    tx.object(shared_object_id),
    tx.object(publisher_id),
    tx.pure.address(targetAddress),
]);

https://github.com/sentioxyz/typemove/blob/main/packages/sui/src/tests/move-call.test.ts#L44
in 1.6.8, I use devInspect to simulate the same tx and it works ok, but will try with the example you provided

it suppose to work like:

if it pass an tx argment, e.g. tx.object, then just use original object, if not, then try to wrap it with correct type

I tried to use view instead of builder as you did in the test, pass the args without tx declaration, and devInspect does give me a VMVerificationOrDeserializationError in command 0

  const res = await module.view.authorizeApi(client, [
    shared_object_id,
    publisher_id,
    targetAddress,
  ]);

got it, will look deeper

image this is because when we we do view function, we put transation instance creation inside, so basically we can't object object as argument.

Nevertheless, I believe we found the root issue that causes previous issue, try 1.6.11, use like this:

    const arg = airdrop.builder.authorizeApi(tx, [
      tx.object('0x080c14c97f457e8d40036109e647376beef62d3de35b51a3b9d183295fc8dc1c'),
      tx.object('0x53a38614e77a540d037c3edea864d0fc5bbe8f5049230b6e1ce173f76596357f'),
      tx.pure.address(SENDER)
    ])
    ```
    
   or
   const arg = airdrop.builder.authorizeApi(tx, [
  '0x080c14c97f457e8d40036109e647376beef62d3de35b51a3b9d183295fc8dc1c',
  '0x53a38614e77a540d037c3edea864d0fc5bbe8f5049230b6e1ce173f76596357f',
  SENDER
])
```

or mixed

Yeap, it's good now! I used both ways that you suggested and no issue. Thank you!