Task: Atomic Transfer Badge

Task: Atomic Transfers

In this task, you will trade in 5 of the DevCoins you earned in this task, for a new shiny Atomic Transfer Badge. You will do this by creating… :drum:, you named it, an Atomic Transfer between these two Assets.

We have placed the Atomic Transfer Badges into an Algorand Smart Contract Account with logic that allows you to trade in 5 DevCoins in exchange for 1 badge. It’s up to you to create this group transaction, submit it, and earn a badge!

The Smart Contract Account that has the badges is here: MJXF5ZZAIZKHEHQD324UEWXID3TQMPOF7P52PXSCEJBH66TKNATAEVMIHU.

Its associated LogicSig can be accessed here: https://github.com/algorand-devrel/AlgorandDeveloperTasks/blob/master/Contracts/atb.lsig
TEAL script: https://github.com/algorand-devrel/AlgorandDeveloperTasks/blob/master/Contracts/atb.teal

Task instructions

  1. Make sure you have completed the Task: Opt-in to ASAs to get 10 DevCoins.
  2. Opt-In to the Atomic Transfer Badge Asset. Details here.
  3. Create a group transaction with 2 transactions (order matters). - Transaction 1: Asset Transfer of 5 DevCoins from you to the Smart Contract account - Transaction 2: Asset Transfer of 1 AtomicTransferBadge from the Smart Contract Account to you.
  4. Group these transactions, i.e. calculate the group ID and assign it to each transaction before signing.
  5. Sign the first transaction with your secret key.
  6. Attach the LogicSig to the second transaction as its signature.
  7. Submit them together to the network.
  8. If done right, the badge will belong to you in less than 5 seconds!

Prerequisites

  1. Read how Atomic Transfers work here.
  2. Check out how to use Algorand Standard Assets in Go, Java, JavaScript, and Python.

Hint

This Atomic Transfer is enabled by a Smart Contract account, but it does not require you to author or change any contracts. We will save that for another time!

All you need to know is that in order to sign the transfer from the Smart Contract account, you must attach the program bytes, called the LogicSig. Those can be found here.

Why This Task Matters

Atomic Transfers paired with Algorand Smart Contracts mean that you can create logic that will allow anyone to trade for your asset provided they give you something in return. Once the contract is funded, this is essentially a no-touch solution for the seller and a single group transaction for the buyer. This task is very similar to a limit-order trade.

The combination of Assets, Atomic Transfers, and Layer-1 Smart Contracts can be very powerful and makes transfers like these very fast and secure. This is just one of the many different use cases that is made possible by these new features.

Have Questions?

Post them here!

Atomic Transfer Badge:

Dear @liz,

My problems:

  1. When I try to use atb.lsig to sign the second transaction, I get an error:
./goal clerk sign -i txsplit-1 -o txsplit-1.sig -L atb.lsig -d data_testnet
atb.lsig: decode failed, msgpack decode error [pos 1]: only encoded map or array can be decoded into a struct
  1. https://github.com/algorand-devrel/AlgorandDeveloperTasks/raw/master/Contracts/atb.lsig is not an lsig, but a compiled teal program, atb.teal.tok

  2. When I disasemble the atb.lsig file, there is a bnz label2, but there is no label2: in the disassembled file.

Good questions.

  1. Since this is an Asset Transfer Txn, you first need to use the goal asset send command to create the transaction and output it to a file. Do all the required grouping. Then use goal clerk sign on each split transaction. The flow is a little clunky in goal right now but doable. Note that I have added the atb.teal file so that you can use the -p flag with goal clerk sign.

I would also encourage you to try this with the SDKs:

Here’s an example of reading the program bytecode with Python and creating a LogicSig object:

bytecode = open('atb.lsig', 'rb').read()
lsig = transaction.LogicSig(bytecode)
lsig.address() # Should match the address in the task
  1. Yes, you are technically correct. The .lsig file is just the raw bytecode of the program. An lsig within a transaction is a message pack struct of those TEAL bytes. The SDKs and goal will convert this accordingly. Here’s what that lsig ends up looking like within a transaction using goal clerk inspect
{
  "lsig": {
    "l": "// version 1\nintcblock 1 2 4 0 10623 3797 5 20000000 3445000 2000\nbytecblock 0x7a23977d32ab2afcfd7a5c7f62ca604deca1eac1f6902153ae4b9fc11449de84 0x0000000000000000000000000000000000000000000000000000000000000002 0x0000000000000000000000000000000000000000000000000000000000000001\nglobal GroupSize\nintc_0\n==\nbnz label1\nglobal GroupSize\nintc_1\n==\ntxn GroupIndex\nintc_0\n==\n&&\ntxn TypeEnum\nintc_2\n==\n&&\ngtxn 0 TypeEnum\nintc_2\n==\n&&\ntxn AssetAmount\nintc_3\n>\n&&\ntxn XferAsset\nintc 4\n==\n&&\ngtxn 0 XferAsset\nintc 5\n==\n&&\ntxn AssetSender\nglobal ZeroAddress\n==\n&&\ntxn AssetCloseTo\nglobal ZeroAddress\n==\n&&\ngtxn 0 AssetReceiver\ntxn Sender\n==\n&&\ngtxn 0 AssetAmount\nintc_0\nmulw\nstore 2\nstore 1\ntxn AssetAmount\nintc 6\nmulw\nstore 4\nstore 3\nload 1\nload 3\n>\nbnz label2\nload 1\nload 3\n==\nload 2\nload 4\n>=\n&&\nbnz label3\nerr\nlabel1:\ntxn TypeEnum\nintc_0\n==\ntxn CloseRemainderTo\nbytec_0\n==\n&&\ntxn FirstValid\nintc 7\n>\n&&\ntxn Receiver\nglobal ZeroAddress\n==\n&&\ntxn Amount\nintc_3\n==\n&&\ntxn TypeEnum\nintc_2\n==\ntxn AssetSender\nglobal ZeroAddress\n==\n&&\ntxn AssetAmount\nintc_3\n==\n&&\ntxn XferAsset\nintc 5\n==\ntxn XferAsset\nintc 4\n==\n||\n&&\ntxn AssetCloseTo\nbytec_0\n==\ntxn FirstValid\nintc 7\n>\n&&\ntxn AssetReceiver\nglobal ZeroAddress\n==\n&&\ntxn AssetReceiver\ntxn Sender\n==\ntxn AssetCloseTo\nglobal ZeroAddress\n==\n&&\ntxn Lease\nbytec_1\n==\ntxn Lease\nbytec_2\n==\n||\n&&\ntxn LastValid\nintc 8\n<\n&&\n||\n&&\n||\nlabel3:\ntxn Fee\nintc 9\n<=\n&&\n"
  }
  1. This is due to a bug in the decompiler: https://github.com/algorand/go-algorand/issues/604

Here are some tips if you are using the SDKs:

  1. When signing the AssetTransferTxn from your account to the contract account, you will use the traditional sign method with your secret key.
  2. When signing the transaction from the contract account you will use the LogicSigTxn construct to produce the lsig field instead of the sig field produced above.

Check out how to create Asset Transfer Txns in the SDKs here:
https://developer.algorand.org/docs/asa-sdk-usage

And see how to sign transactions that are from a contract account here:
https://developer.algorand.org/docs/asc1-sdk-usage

Dear @liz,
Thank you.

1 Like

Hi Liz, Thanks for this tutorial. Not sure I understand fully but I was able to execute this successfully. :slight_smile: Here’s the transaction IDs

Account address: QA75IQ76F6H2T55G65BY7BPLF5QNWSLT5XGI62COZSYB4ZQ3MSKI3EQ25A

1st TX ID: AULRPUYSI24MBK45MO7FT44GTQQX4JILPSITLNT2T7VZM5S2F3SQ
2nd TX ID: XPKW5QYFH5K5QTTYLRH6D5HYWMQDZSRV3637MNI2HCKDPEQ47RNA

I did 2 times just to see if I can get 2 badges and I did. 3rd time it failed because of “underflow on subtracting 5” which is right.

Now here are the questions.

  1. I still have hard time understanding TEAL :frowning: . Do you have commented atb.teal so that I can understand each line?

  2. When I queried asset 10623 through pure stake API i did not see contract account in there. creator, manager, reserve (all are with address starting PIR…). How then the smart contract can execute the transaction to transfer asset?

Thanks

Nice!

  1. We will be publishing more and more TEAL templates that represent common types of scenarios and then surfacing them through the SDKs as function calls. That way you do not have to worry about writing a TEAL script, but instead you can just plug in the values that matter to you. Those will be added to this page once the SDK support is available: https://developer.algorand.org/docs/algorand-smart-contract-templates . If you prefer not to wait, you can check out some TEAL templates before they make it to our SDKs here: https://github.com/algorand/go-algorand/tree/master/tools/teal/templates . The atb.teal script is based off of the limit-order-b template. There is some extra walkthrough documentation here: https://github.com/algorand/go-algorand/tree/master/tools/teal/templates/docs . If you’re writing your own TEAL scripts check out these guidelines. We highly recommend you use existing templates and/or share your code with the community or others to audit to ensure it has no unintended consequences.

  2. The contract account does not need to be assigned to the asset in any way. It just needs to opt-in to the asset like any account would have to. To do this, it needs to have this opt-in transaction built into its logic. Then someone needs to issue an opt-in transaction on its behalf. In the atb.teal script, this can only happen before a specific round which has already passed now. We were the ones who issued that transaction, but anyone could have done it if they had the contract. Once it opts in, anyone can send it the asset it opted-in for (just like anyone can send the account algos). Before we posted the task, we transferred 100 ATBs into the contract account. You could also transfer any number of ATBs or DevCoins into that account since it has opted into both assets.

I know this is a lot. We will be rolling out more docs and tasks on TEAL in the future to help break this down further!

By the way, did you use the SDKs (and which one?) or goal?

Thanks Liz. parameterized TEAL templates will be good so that we simply use it by changing asset IDs and addresses. I will recreate this again on my own to see if I can understand. I used both Goal and Java SDK to trigger the transaction. Thanks for coming up with these tasks. It forces me learn.

1 Like

Am I correct, when I say that there is no ASC1 contract code stored on the bockchain?
It is an abstraction existing only in our heads, and not a physical entity like in the case of Ethereum
blockchain?

When we use TEAL compiled code in the transactions, it can either be unsigned or signed.

When it is unsigned, the “from address” (“snd”) must be the same as the hash of the compiled code,
i.e. the “contract address”?

When it is signed, is must be signed with the private key of the sender, and it enables or disables the transaction(s) by executing the “contract code”?

Using goal exclusively, I was able to exchange my 5 DevCoins for the Atomic Transfer Badge asset. However, I learned the hard way about the importance ordering transaction into a proper grouped transactions and signing their components prior to sending.

My misstep was signing both transactions without first using goal clerk group to assign the "grp" field to both transactions. This is required, because that command links the two transactions by assigning the same "grp" value to both. Importantly, the signature must include the "grp" data to enforce the linkage.

Take a look at your initial unsigned transaction and then compare it after your group them:

goal clerk inspect tx1.utxn
goal clerk group -i tx1.utxn -o gtx.utxn -d testnet
goal clerk inspect gtx-0.utxn

Notice that gtx-0.utxn includes the "grp" field which is lacking from tx1.utxn

In my case, I signed the two transactions and assembled them manually. Unfortunately for me, they were not linked as a proper group transaction, just two transactions ordered in the file sent by rawsend. Therefore, TX1 executed and sent my DevCoins to the escrow and then TX2 failed because the TEAL code checks for fields in gtxn[0] which do not exist, because it’s actually looking at my TX2 not TX1 as expected. It’s not atomic if you don’t create, order, group and sign your transactions properly.

Learning is fun! I hope this helps you avoid a pitfall as you attempt to solve this puzzle.