Byte to int (vice versa) conversion and comparison in Teal

I’m trying to understand how data types work in Teal. As I see, Int is represented with big endian to uint64. For example, if my argument string is “1”, btoi opcode converts it to 49.

The problem I faced is checking AssetID in the Smart Contract. From this example https://github.com/algorand/smart-contracts/blob/master/devrel/poi/poi.teal#L226, this code always fails:

Let’s say AssetID equals 1. It is first converted and saved in global storage, line 19:

byte "AssetID"
txna ApplicationArgs 0
btoi
app_global_put // Value 49

Later, it is retrieved and stored in scratch space on line 202:

// get asset id from global
int 0 //current app
byte "AssetID"
app_global_get_ex
bz failed
// store assetid
store 11

When it’s time to check AssetID, saved value doesn’t not equal to the value of XferAsset opcode, which returns 1.

gtxn 1 XferAsset // 1
load 11 //49
==

What is the solution here?

Let’s see:

$ cat btoi.teal
byte "1"
btoi
int 49
==
byte "123"
btoi
int 0x313233
==
&&
byte 0xffff
btoi
int 65535
==
&&

$ cat comp.sh
#!/bin/bash
gcmd="~/n_test1/goal -d ~/n_test1/data_testnet"

CONTRACT_ADDR=`eval $gcmd clerk compile btoi.teal | awk '{ print $2 }'`
echo "Contract address: $CONTRACT_ADDR"

eval $gcmd clerk send -f $CONTRACT_ADDR -t $CONTRACT_ADDR -a 0 -o tx0.tx
eval $gcmd clerk sign -p btoi.teal -i tx0.tx -o stx0.tx
eval $gcmd clerk dryrun -t stx0.tx

$ ./comp.sh
Contract address: 6L6I7HYXILLTALWBVYP5BSL7QHDWB22ZEFGFEKPKGS6LAYKBXO5T4KI3UY
Please enter the password for wallet 'w1': 
tx[0] cost=16 trace:
  1 intcblock => <empty stack>
 11 bytecblock => <empty stack>
 22 bytec_0 => (31) 
 23 btoi => (49 0x31) 
 24 intc_0 => (49 0x31) 
 25 == => (1 0x1) 
 26 bytec_1 => (313233) 
 27 btoi => (3224115 0x313233) 
 28 intc_1 => (3224115 0x313233) 
 29 == => (1 0x1) 
 30 && => (1 0x1) 
 31 bytec_2 => (ffff) 
 32 btoi => (65535 0xffff) 
 33 intc_2 => (65535 0xffff) 
 34 == => (1 0x1) 
 35 && => (1 0x1) 

 - pass -


OK. That completely makes sense. But my question was from the example above. How do you assert that AssetID sent from atomic transfer equals to AssetID you saved in the beginning. XferAsset returns int 1, not byte… so can’t use btoi

gtxn 1 XferAsset // 1
load 11 //49
==

If the Application Arg is given as byte when calling the app, then btoi converts to uint64, ans XferAsset also gives back uint64.

--app-arg strings            Args to encode for application transactions (all will be encoded to a byte slice). For ints, use the form 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'.

So… it should work, in theory

Right, thanks! I think know the issue.

The code I’m debugging creates Stateful contract from SDK. Thus, passing args like this:

appArgs.push(new Uint8Array(Buffer.from("myString")));
appArgs.push(new Uint8Array(Buffer.from(assetID.toString())));

This makes AssetID arg in Teal as byte and 1 becomes 49 after btoi.

On the other hand with goal cli --app-arg "int:1" arg keeps value as 1. As a result this block becomes valid:

gtxn 1 XferAsset // 1
load 11 //1
==

I can’t find the equivalent SDK code for int type arg (--app-arg "int:1") in the documentation. I see the example for Stateless contract: args.push(algosdk.encodeUint64(123)); but this gives the error
err TypeError: algosdk.encodeUint64 is not a function.

1 Like

Make sure you have 1.9.1 of the SDK. This was recently updated with the new encodeUint64 function.

1 Like

Awesome! thanks. Updating SDK from 1.8.1 to 1.9.1 solved the issue with the encodeUint64 function.