Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Observation in passing buffer values to contractCall #786

Closed
whoabuddy opened this issue Jan 20, 2023 · 3 comments · Fixed by #772
Closed

Observation in passing buffer values to contractCall #786

whoabuddy opened this issue Jan 20, 2023 · 3 comments · Fixed by #772
Assignees

Comments

@whoabuddy
Copy link
Contributor

This started with testing a simple Clarity contract that can stack its own STX through pool delegation.

The related test simulates the deposit and stacking functions, but there was a discrepancy in understanding how to pass the correct buffer value.

In the end this is resolved, but documenting it here because the result was unexpected.

The Short Story

I had no idea I needed to pass the hex string for tx options related to Clarity buffers, and it took quite a while to figure out.

The Long Story

There were two function parameters that needed to be (buff 1) and (buff 20) to represent the pox address in delegate-stx.

The hex values were each stored in a constant:

const poxVer = "0x01";
const poxHash = "0x13effebe0ea4bb45e35694f5a15bb5b96e851afb";

My first instinct was to use types.buff() on the function arguments since it's normally required to match the Clarity type.

This resulted to mismatched buffer sizes and appears to be the cause for the error in reported in Discord. @hugocaillard that is now fixed (although the print errors remain).

It's also inconsistent with how makeContractCall() operates here (not sure for stacks.js version), which expects a Clarity buffer as part of the tx options.

Not sure what the fix would be but wanted to at least document the struggle to figure it out!

Relevant TX:
https://explorer.stacks.co/txid/0xdbbecf8113b530bd12ca67c425cc1c27a0bf7916ded65e106cf0ecc8fb28c75a?chain=mainnet

@hugocaillard
Copy link
Collaborator

I tried to document / improve the use types.buff().

When a contract function that takes a buffer as an arguments, it's called hex string starting with 0x.
And the types.buff function is aiming to help developer with that.

I think it could lead to confusion that types.buff(val: ArrayBuffer | string) would accept a string because if it's called with types.buff("0x01"), it would interpret it as an utf8 string (while it's already the hex value of the data)

So I introduced a new function types.stringToBuff(val: string) with the following idea:

  • it accepts a string, converts it to to a buffer of bytes and then convert to an hex string.
  • if the string input starts with 0x, return it as it is (it's a different behavior compared to types.buff, but calling types.buff("0x01") would most likely lead to unexpected results

The type.buff(val: ArrayBuffer | string) still work as expected (no breaking changes) but its signature is now marked as deprecated in favor of types.buff(val: Uint8Array)

@whoabuddy
Copy link
Contributor Author

Thank you! I think that makes things clearer, and in the end knowing the hex string is the expected result was the part I didn't understand.

When I saw the Clarity type was buffer my assumption was that I had to use ArrayBuffer | Uint8Array, but I also knew the hex string was what I saw in past successful contract calls.

With the changes above, both passing the hex string directly and using types.stringToBuff() would produce the same result, except to me the latter is much clearer and inline with other values passed to Tx.contractCall e.g.

This is valid and works, but it is unclear why the values are hex strings and that the contract is expecting a buff:

Tx.contractCall(
  "stacking-action",
  "stack-stx",
  [
    types.uint(amount),
    types.principal(pool.address),
    "0x01",
    "0x13effebe0ea4bb45e35694f5a15bb5b96e851afb",
    types.none(),
  ],
  admin.address
)

This would be the new version, much cleaner:

Tx.contractCall(
  "stacking-action",
  "stack-stx",
  [
    types.uint(amount),
    types.principal(pool.address),
    types.stringToBuff("0x01"),
    types.stringToBuff("0x13effebe0ea4bb45e35694f5a15bb5b96e851afb"),
    types.none(),
  ],
  admin.address
)

The pattern used here may be helpful with this issue in micro-stacks too, the more consistent we can make everything the better!

@hugocaillard
Copy link
Collaborator

@whoabuddy
Thanks for your feedbacks.

When I saw the Clarity type was buffer my assumption was that I had to use ArrayBuffer | Uint8Array

If you look at the Tx.contractCall signature, the third param is args: Array<string>, so you only pass strings.
The types.<type> methods are helpers to transform JS values into strings that clarinet will interpret as a Clarity values.

So

  [
    types.uint(10),
    types.principal("ST98...FG"),
    types.stringToBuff("0x01"),
    types.stringToBuff("0x13effebe0ea4bb45e35694f5a15bb5b96e851afb"),
    types.none(),
  ]

Could be

  [
    "u10",
    "'ST98...FG", // notice the extra `'`
    "0x01",
    "0x13effebe0ea4bb45e35694f5a15bb5b96e851afb",
    "none",
  ]

It would have the exact same effect. But I agree that working with buff can be confusing so the least we can do is add some docs and easy to use helpers 👍

@hugocaillard hugocaillard moved this from 🏗 In progress to 👀 In review in DevTools Feb 27, 2023
@github-project-automation github-project-automation bot moved this from 👀 In review to ✅ Done in DevTools Mar 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

2 participants