Serialize smart contract param

Hello,

I’m trying to call a smart contract method from another smart contract but having issues serializing the param

This is the payload I’m trying to serialize to pass in as data in the simple_withdraws

{
  "id": "<String>",
  "payer": "<String of size 64 containing lowercase hex characters.>",
  "receiver": "<String of size 64 containing lowercase hex characters.>"
}

Hi there
Can you share a bit more?
The Rust type that you use for the parameter and the code where you serialize it and call the other smart contract could be helpful

#[derive(Serial,Deserial,SchemaType)]
pub struct PayParam {
   /// id of the transaction offchain 
   pub id: String,
   /// payer of the transaction
   pub payer: PublicKeyEd25519,
   /// receiver of the transaction
   pub receiver: PublicKeyEd25519,
}

This seems fine so far.
Could you also share the code where you use something of this type to call another smart contract?

async pay(amount: number, recipient: string, wallet: Key) {
      const contractAddress = { index: this.contractIndex, subindex: this.contractSubindex };
      const sender = new AccountAddress(this.sender);
      const signer = buildBasicAccountSigner(this.signingKey);
      const moduleRef = new ModuleReference(this.moduleRef);
      const schema = await this.client.getEmbeddedSchema(moduleRef);
  
      const maxCost = BigInt(30000);
      const contractName = 'gonana_smart_wallet';
      const receiveName = 'gonana_smart_wallet.withdrawCcd';
  
    
  
      const nonce = (await this.nonceOf(wallet.publicKey))[0]
  
      //console.log({nonce})
      const expiry_time = await this.getExpiryTime()
      const data = {
          "id": "uyf",
          "payer": wallet.publicKey,
          "receiver": recipient
      }
  
      const message = {
          entry_point: "withdrawCcd",
          expiry_time,
          nonce,
          service_fee_amount: new CcdAmount(0),
          service_fee_recipient: "b288c8518c8be158e5e22cb1ee8c748b1992a2cb3572643a7b6ceb1ccd6bf3ec",
          simple_withdraws: [
            {
              data: JSON.stringify(data),
              to: {
                  Contract: [{
                      index: 9832,
                      subindex: 0
                  }, 'pay']
              },
              withdraw_amount: new CcdAmount(amount * (10 ** 6)),
            }
          ]
      }
  
      const messageHash = await this.getCcdWithdrawMessageHash(message)
      // const privateKeyUint = sodium.from_hex(wallet.privateKey)
      const messageHashBin = this.hexToUint8Array(messageHash);
  
      // const messageHashBin = sodium.hex2bin();
      const privateKeyBin = this.hexToUint8Array(wallet.privateKey);
  
      const signatureUint8 = sodium.crypto_sign_detached(messageHashBin, privateKeyBin);
      const signature = sodium.to_hex(signatureUint8);
      const paramJson = [
          {
            message,
            signature,
            signer: wallet.publicKey
          }
        ]
        
      const updateHeader: AccountTransactionHeader = {
          // @ts-ignore
          expiry: await this.getDefaultTransactionExpiry(),
          nonce: (await this.client.getNextAccountNonce(sender)).nonce,
          sender,
      };
  
      const updateParams = serializeUpdateContractParameters(
          contractName,
          'withdrawCcd',
          paramJson,
          schema,
      );
      console.log({updateHeader, updateParams})
  
      const updatePayload: UpdateContractPayload = {
          amount: new CcdAmount(0),
          address: unwrap(contractAddress),
          receiveName,
          message: updateParams,
          maxContractExecutionEnergy: maxCost,
      };
  
      console.log({updatePayload})
      const updateTransaction: AccountTransaction = {
          header: updateHeader,
          payload: updatePayload,
          type: AccountTransactionType.Update,
      };
  
      const updateSignature = await signTransaction(updateTransaction, signer);
      const updateTrxHash = await this.client.sendAccountTransaction(
          updateTransaction,
          updateSignature
      );
      console.log('Transaction submitted, waiting for finalization...');
      const updateStatus = await this.client.waitForTransactionFinalization(updateTrxHash);
      console.dir(updateStatus, { depth: null, colors: true });
  
      return updateTrxHash
  
}```

The issue here is changing the javascript object to string, the data field expects a string in hex; when we pass an empty string the transaction goes through and fails on chain because of a parse param error as expected. But if we try to stringify the data object, the serialization fails with the sdk and throws this error

Error: Unable to serialize parameters, due to: "simple_withdraws" -> 0 -> "data" -> Odd number of digits
    at module.exports.__wbindgen_error_new (/Users/mac/con-test/node_modules/@concordium/rust-bindings/pkg/node/concordium_rust_bindings.js:1409:17)
    at wasm://wasm/00748e9a:wasm-function[1054]:0x15c788
    at Object.module.exports.serializeReceiveContractParameters (/Users/mac/con-test/node_modules/@concordium/rust-bindings/pkg/node/concordium_rust_bindings.js:511:14)
    at serializeUpdateContractParameters (/Users/mac/con-test/node_modules/@concordium/common-sdk/lib/serialization.js:323:39)
    at ConcordiumService.getCcdWithdrawMessageHash (/Users/mac/con-test/index.ts:446:67)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async ConcordiumService.pay (/Users/mac/con-test/index.ts:573:27)
    at async main (/Users/mac/con-test/pay.ts:13:17)
TypeError: Cannot read properties of undefined (reading 'length')
    at ConcordiumService.hexToUint8Array (/Users/mac/con-test/index.ts:381:46)
    at ConcordiumService.pay (/Users/mac/con-test/index.ts:575:35)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async main (/Users/mac/con-test/pay.ts:13:17)

I see.

So "data" expects a hex string encoding of your parameter, and you are currently passing the stringified json.
Instead, you need to use the schema of the contract which defines this simple_withdraws to serialize the json (data variable) into bytes and pass this as a hex string to the "data" field.

okay, makes sense. Thank you