ASA HTLC - no closure of the contract

Hi, I’m testing the ASA_HTLC TEAL example provided at go-algorand/asa-htlc.teal.tmpl at template-asa-htlc · ryanRfox/go-algorand · GitHub

I’m using the Go SDK and I’m able to compile the TEAL program and I’m following (in Go) alla the steps reported at: go-algorand/asa-htlc.teal.md at template-asa-htlc · ryanRfox/go-algorand · GitHub

I’m able to complete all the steps whithout any error but when the Receiver send the Preimage via the LogicalSignature of smart contract the assets are not trasfered back to the Receiver and the ASA accumulates in the ESCROW account.

Using PureStack GoalSekeer I can see that the correct Preimage is sent into arg[0] and I doublechekced that my TEAL program contains the corresponding Hash (sha256).

arg 0
sha256
byte b64 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f

just and example you can check the ESCROW address: 2BUSJSYFTTPHBP5H43MAX7AO7KPAWVRWD4BHV562VRNQEE72TP7FQRNWNE
and this transaction: L4U6XKXXIW7XQIM6SV7JO5KRNCMVQND3L2DSDCC7WVYQMF4DAT3Q

I generated the sha256 in this way:
the preimage is : preimage
and the corresponding sha256 is: 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f

Can some one help me in understanding why the ASA are not trasfered back to che receiver?
KR
Pietro

My guess is that the issue stems from the fact that:

107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f

is in hexadecimal, while it’s expecting a base64 string.

To convert hexadecimal to base64 in command line:

xxd -p -r <<< 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f | base64

which gives:

EHZhE08h/HwCIj1Qq56zYAvD/8NxJCOh5Hux+anb9V8=

A good solution to understand what is happening is storing the transaction in a file and then running goal clerk dryrun on it. You would see the mismatch there.

Thanks for your help. I corrected the HASH in the teal code inserting the b64 string.
But the transaction does not close the contract. I guess I’m missing some step.
This is the code I use:


//string parameter (R = preimage)

    args = make([][]byte, 1)

    args[0] = []byte("preimage")

    lsig, err = crypto.MakeLogicSig(program, args, sk, ma)

    addr = crypto.LogicSigAddress(lsig).String()

    amount = uint64(0)

    closeRemainderTo = ""

    txn, err = transaction.MakeAssetTransferTxn(addr, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ", amount, note, txParams, closeRemainderTo, TIPSIUM_assetID)

    if err != nil {

        fmt.Printf("Failed to send txn: %s\n", err)

        return

    }

    txID, stx, err = crypto.SignLogicsigTransaction(lsig, txn)

    if err != nil {

        fmt.Printf("Signing failed with %v", err)

        return

    }

    fmt.Printf("Signed tx: %v\n", txID)

    // Submit the raw transaction to network

    transactionID, err = algodClient.SendRawTransaction(stx).Do(context.Background())

    if err != nil {

        fmt.Printf("Sending failed with %v\n", err)

    }

    // Wait for transaction to be confirmed

    waitForConfirmation(txID, algodClient)

    fmt.Printf("Transaction ID: %v\n", transactionID)

    // Save the signed object to disk

    fmt.Printf("Made signed transaction with TxID %s\n", txID)

    filename := "./signed.tx"

    err = ioutil.WriteFile(filename, stx, 0644)

    if err != nil {

        fmt.Printf("Failed in saving transaction to file %s, error %s\n", filename, err)

        return

    }

    fmt.Printf("Saved signed transaction to file: %s\n", filename)

And this is the output of the dryrun command:
[pietro@localhost ~]$ goal clerk dryrun -t signed.tx
tx[0] trace:
1 intcblock 4 0 40665414 20000000 32 1000 =>
16 bytecblock 0x842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82 0x7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827 0x107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f =>
117 txn TypeEnum => (4 0x4)
119 intc_0 // 4 => (4 0x4)
120 == => (1 0x1)
121 txn AssetSender => (0000000000000000000000000000000000000000000000000000000000000000)
123 global ZeroAddress => (0000000000000000000000000000000000000000000000000000000000000000)
125 == => (1 0x1)
126 && => (1 0x1)
127 txn AssetCloseTo => (0000000000000000000000000000000000000000000000000000000000000000)
129 global ZeroAddress => (0000000000000000000000000000000000000000000000000000000000000000)
131 == => (1 0x1)
132 && => (1 0x1)
133 txn AssetAmount => (0 0x0)
135 intc_1 // 0 => (0 0x0)
136 == => (1 0x1)
137 && => (1 0x1)
138 txn XferAsset => (40665414 0x26c8146)
140 intc_2 // 40665414 => (40665414 0x26c8146)
141 == => (1 0x1)
142 && => (1 0x1)
143 dup => (1 0x1) (1 0x1)
144 bnz 197 => (1 0x1)
197 txn Fee => (1000 0x3e8)
199 intc 5 // 1000 => (1000 0x3e8)
201 <= => (1 0x1)
202 && => (1 0x1)

  • pass -

The transaction I tested is LVI3ZXRX7K7SQE5XE5WZWL3H7POQH7RSWIOPOVQO5HNWWPLOUANQ on testnet.
And I can see the correct arg[0] = preimage (in b64)

ASSET TRANSFER

SENDER

[ H4RZG7FXH2RBVDQ2EGPTW24KAPSSPC3K76X37INQX52DEPNPMVDENTHDWE ]

RECIEVER

[ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ ]

ASSET

[40665414 | tipsium]

AMOUNT

0 tipsium

LOGIC SIGNATURE

LOGIC

ASAGBADGgrITgNrECSDoByYDIIQrV2ucW5V8eHVGsh+GLWB21cuDywrhmaDQ9X1uQt+CIH0VEdNWSnNxnI585zCBsjbCZHniCti02VpuVXes6tgnIBB2YRNPIfx8AiI9UKues2ALw//DcSQjoeR7sfmp2/VfMRAiEjETMgMSEDEVMgMSEDESIxIQMREkEhBJQAAySDEJKBIxBygSEDEVKBIxFCgSEBExAiUNEElAABVIMRUpEjEUKRIQLRUhBBIQLQEqEhAxASEFDhA=

ARG 0

cHJlaW1hZ2U=

It looks like you managed to make the transaction:
https://testnet.algoexplorer.io/tx/LVI3ZXRX7K7SQE5XE5WZWL3H7POQH7RSWIOPOVQO5HNWWPLOUANQ

The transaction you made was to allow opt-in to the asset:
scenario 1 in go-algorand/asa-htlc.teal.md at template-asa-htlc · ryanRfox/go-algorand · GitHub

and you did it succesfully.

Note however you did not need to provide the pre-image for that and you should not have done it as it reveals it to everyone, hence makes it insecure.

Essentially, you did steps 4-6 of escrow account creation and funding: go-algorand/asa-htlc.teal.md at 9eaee2d0874833e078cc2f050222d7b4a0563695 · ryanRfox/go-algorand · GitHub
which does not require the pre-image

Now, you need to do the following steps:

(Instructions use goal but you can easily do it in Go too)

Thanks Fabrice but actually my transaction was ment to Close the contract providing the preimage it was the last step. Before this I already sent the ASA to the escrow. The transaction i’m testing is “from” the receiver to receive the funds from the escrow.
As far as I know any transaction providing the right preimage will result in the ASA transfered to the receiver account inserted into the TEAL program. Is this correct? (and the transaction shoul be to the Algo zeroaddress and with amount =0 as)

Stateless TEAL (aka smart signatures) only approve or reject transactions. It does not modify them.

You generated the transaction as follows:

    txn, err = transaction.MakeAssetTransferTxn(addr, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ", amount, note, txParams, closeRemainderTo, TIPSIUM_assetID)

That is, an asset transfer of amount 0 to the address "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ". closeRemainterTo being empty, it is not used.

This is exactly what you see Algorand Transaction

If you want to receive all the funds, the transaction is different.
You want the receiver AND the close remainder to to be the address that will receive funds.
Something like:

    txn, err = transaction.MakeAssetTransferTxn(addr, "QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU", amount, note, txParams,"QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU", TIPSIUM_assetID)

(not tested, you may need to adjust)

Dear Fabrice, I guess there are some “encoding isseue” in my code:

This is the new TRX:

//string parameter (R = preimage)

args = make([][]byte, 1)

args[0] = []byte("preimage")

lsig, err = crypto.MakeLogicSig(program, args, sk, ma)

addr = crypto.LogicSigAddress(lsig).String()

amount = uint64(1)

closeRemainderTo = ""

txn, err = transaction.MakeAssetTransferTxn(addr, "PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE", amount, note, txParams, closeRemainderTo, TIPSIUM_assetID)

if err != nil {

    fmt.Printf("Failed to send txn: %s\n", err)

    return

}

txID, stx, err = crypto.SignLogicsigTransaction(lsig, txn)

if err != nil {

    fmt.Printf("Signing failed with %v", err)

    return

}

fmt.Printf("Signed tx: %v\n", txID)

// Submit the raw transaction to network

transactionID, err = algodClient.SendRawTransaction(stx).Do(context.Background())

if err != nil {

    fmt.Printf("Sending failed with %v\n", err)

}

// Wait for transaction to be confirmed

waitForConfirmation(txID, algodClient)

fmt.Printf("Transaction ID: %v\n", transactionID)

// Save the signed object to disk

fmt.Printf("Made signed transaction with TxID %s\n", txID)

filename := "./signed.tx"

err = ioutil.WriteFile(filename, stx, 0644)

if err != nil {

    fmt.Printf("Failed in saving transaction to file %s, error %s\n", filename, err)

    return

}

fmt.Printf("Saved signed transaction to file: %s\n", filename)

type or paste code here


And this this the result of the dryrun on it
[pietro@localhost ~]$ goal clerk dryrun -t signed.tx
tx[0] trace:
  1 intcblock 4 0 40665414 20000000 32 1000 => <empty stack>
 16 bytecblock 0x842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82 0x7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827 0x107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f => <empty stack>
117 txn TypeEnum => (4 0x4)
119 intc_0 // 4 => (4 0x4)
120 == => (1 0x1)
121 txn AssetSender => (0000000000000000000000000000000000000000000000000000000000000000)
123 global ZeroAddress => (0000000000000000000000000000000000000000000000000000000000000000)
125 == => (1 0x1)
126 && => (1 0x1)
127 txn AssetCloseTo => (0000000000000000000000000000000000000000000000000000000000000000)
129 global ZeroAddress => (0000000000000000000000000000000000000000000000000000000000000000)
131 == => (1 0x1)
132 && => (1 0x1)
133 txn AssetAmount => (1 0x1)
135 intc_1 // 0 => (0 0x0)
136 == => (0 0x0)
137 && => (0 0x0)
138 txn XferAsset => (40665414 0x26c8146)
140 intc_2 // 40665414 => (40665414 0x26c8146)
141 == => (1 0x1)
142 && => (0 0x0)
143 dup => (0 0x0) (0 0x0)
144 bnz 197 => (0 0x0)
147 pop => <empty stack>
148 txn CloseRemainderTo => (0000000000000000000000000000000000000000000000000000000000000000)
150 bytec_0 // addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU => (842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82)
151 == => (0 0x0)
152 txn Receiver => (0000000000000000000000000000000000000000000000000000000000000000)
154 bytec_0 // addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU => (842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82)
155 == => (0 0x0)
156 && => (0 0x0)
157 txn AssetCloseTo => (0000000000000000000000000000000000000000000000000000000000000000)
159 bytec_0 // addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU => (842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82)
160 == => (0 0x0)
161 txn AssetReceiver => (7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827)
163 bytec_0 // addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU => (842b576b9c5b957c787546b21f862d6076d5cb83cb0ae199a0d0f57d6e42df82)
164 == => (0 0x0)
165 && => (0 0x0)
166 || => (0 0x0)
167 txn FirstValid => (17828208 0x1100970)
169 intc_3 // 20000000 => (20000000 0x1312d00)
170 > => (0 0x0)
171 && => (0 0x0)
172 dup => (0 0x0) (0 0x0)
173 bnz 197 => (0 0x0)
176 pop => <empty stack>
177 txn AssetCloseTo => (0000000000000000000000000000000000000000000000000000000000000000)
179 bytec_1 // addr PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE => (7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827)
180 == => (0 0x0)
181 txn AssetReceiver => (7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827)
183 bytec_1 // addr PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE => (7d1511d3564a73719c8e7ce73081b236c26479e20ad8b4d95a6e5577acead827)
184 == => (1 0x1)
185 && => (0 0x0)
186 arg_0 => (707265696d616765)
187 len => (8 0x8)
188 intc 4 // 32 => (32 0x20)
190 == => (0 0x0)
191 && => (0 0x0)
192 arg_0 => (707265696d616765)
193 sha256 => (107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f)
194 bytec_2 // addr CB3GCE2PEH6HYARCHVIKXHVTMAF4H76DOESCHIPEPOY7TKO36VP3FYXBDA => (107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f)
195 == => (1 0x1)
196 && => (0 0x0)
197 txn Fee => (1000 0x3e8)
199 intc 5 // 1000 => (1000 0x3e8)
201 <= => (1 0x1)
202 && => (0 0x0)

REJECT

And this is the TEAL.program :


The TxID is: RFT5NBVA2PAXU2CHCQI22DFHOL23AJ6S326AD4FJM6KL7BKVZ3YA

.
//
txn TypeEnum
int AssetTransfer
==
txn AssetSender
global ZeroAddress
==
&&
txn AssetCloseTo
global ZeroAddress
==
&&
txn AssetAmount
int 0
==
&&
txn XferAsset
int 40665414
==
&&
dup
bnz opt-in_pass
pop
txn CloseRemainderTo
addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU
==
txn Receiver
addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU
== 
&&
txn AssetCloseTo
addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU
==
txn AssetReceiver
addr QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU
== 
&&
||
txn FirstValid
int 20000000
>
&&
dup
bnz time-lock_pass
pop
txn AssetCloseTo
addr PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE
==
txn AssetReceiver
addr PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE
==
&&
arg 0
len
int 32
==
&&
arg 0
sha256
byte b64 EHZhE08h/HwCIj1Qq56zYAvD/8NxJCOh5Hux+anb9V8=
==
&&
time-lock_pass:
opt-in_pass:
txn Fee
int 1000
<=
&&

The TEAL code was missing

Your closeRemainderTo is still incorrect, it needs to be:

closeRemainderTo = "PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE"

It doesn’t work. I tried
In the TEAL code I’m following the example by Ryan just replacing
TMPL_RCV with PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE and
TMPL_OWN with QQVVO244LOKXY6DVI2ZB7BRNMB3NLS4DZMFODGNA2D2X23SC36BIA2X6VU.

I really can’t understand why it is not working.

If I set closeRemainderTo = "PUKRDU2WJJZXDHEOPTTTBANSG3BGI6PCBLMLJWK2NZKXPLHK3ATXNGW5YE" in the code the transaction fail: rejected by logic.