Transaction cannot close account to its sender

The goal is if the teal program executes as true, then it will closeRemainderTo the address that invokes the program. However, when I run it, I get the error: transaction cannot close account to its sender.

Here is the teal program - this returns PASS.

// Push arg_0 to stack
arg_0

// Pop arg_0 from stack and push sha256 hashed result to stack
sha256

// Push pre-computed b64 encoded sha256 of my-Super-Secret-32-bytes-of-Data
byte b64 Q0oWzpC/ICJPtdpY1hPLIZm0E2Ew3ozIGGNbWLg38aQ=

// Pop the top two items from the stack, compare, then push result to stack
==

Then I send it with closeRemainderTo set to the address of the sender:

let txn = {
        "from": recoveredAccount.addr,
        "to": "R3NORFFJ3RXGEER2EUXJH5F35EGOBZYOGP6FNX3KCQ3HJ23VTETQJCM6SA",
        "fee": params.fee,
        "amount": 0,
        "firstRound": params.lastRound,
        "lastRound": endRound,
        "genesisID": params.genesisID,
        "genesisHash": params.genesishashb64,
        "closeRemainderTo": recoveredAccount.addr
    };

    // create logic signed transaction.
    // Had this been an escrow the lsig would not contain the
    // signature but would be submitted the same way
    let rawSignedTxn = algosdk.signTransaction(txn, recoveredAccount.sk);

    //Submit the lsig signed transaction
    let tx = (await algodclient.sendRawTransaction(rawSignedTxn.blob));

Then the error I get back is:

 text: 'transaction cannot close account to its sender 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE',

Is there something I need to add to the program to closeRemainderTo?

Thanks!

The goal of the closeRemainderTo is to avoid cases where you leave small amount of algos. So, if your account goes below a certain threshold, it will automatically get closed and the remainder would go to the specified account. By using the same account for both “from” and “closeRemainderTo”, it looks like you’re trying to work around this logic. ( which is why it’s failing )

Could you try and create two accounts, one for the closeRemainderTo and one for sending transaction from ?

Ohhhh thank you so much for clarifying that.

So just to double check, whenever a program executes and passes, that address tied to the program will pay out all of the balance of the account to the sender of the transaction that invoked the program execution, except for a minimum account balance, which it will not transfer out.

If you want to transfer out 100% of the balance, you must set the CloseRemainderTo address. If the CloseRemainderTo address is set, the sender of the transaction that invoked the program does not get transferred any algos, instead, all the algos are sent to that CloseRemainderTo address.

The CloseRemainderTo address cannot be the same as the sender of the transaction that invoked the program execution.

So if I want to write an escrow program, that when executed successfully pays out all of the balance of the escrow account to the sender of the transaction that invoked the program execution, I cannot do that. The sender would need to have two algorand addresses: one for sending the transaction and one for receiving the fund.

Is that a correct understanding?

When I run the same program with setting a separate CloseRemainderTo account from the from account or without setting a CloseRemainderTo account, I get this overspend error:

'TransactionPool.Remember: transaction ACZTVICIHDRLRAJK6U6K5QV6PUK6BSDS2KX7VGTYDGLSFYNFPI5Q: overspend (account 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE, data {_struct:{} Status:Offline MicroAlgos:{Raw:0} RewardsBase:0 RewardedMicroAlgos:{Raw:0} VoteID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] SelectionID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] VoteFirstValid:0 VoteLastValid:0 VoteKeyDilution:0 AssetParams:map[] Assets:map[]}, tried to spend {1000})

Is there a way to set that the program should pay out the from address everything but the minimum amount so that there is no overspend error?

I believe you are looking for something similar to the ASC1 Escrow Example recently published. It defines a hashed time lock contract (HTLC) where the TEAL program is the escrow. The program checks the CloseRemainderTo field within the send transaction to determine which clause (hash lock or time lock) to evaluate. Notice the TEAL program defines both addresses (sender and receiver) and recall when compiling this program it will yield the escrow account address. Changing either the sender or receiver will yield a different escrow account, therefore a distinct escrow address for those parties (also change the secret for each usage).

Either party may construct a zero-value send transaction using from-program and must include the CloseRemainderTo corresponding to their account (as defined in the TEAL program).

If the Receiver provides the proper argb64 to satisfy the hash lock, the program will evaluate to TRUE and the escrow account will be swept of all algos into their CloseRemainderTo account.

goal clerk send -a 0 --argb64 b64EncodedSecret= -c RECEIVERADDRESS -t RECEIVERADDRESS --from-program htlc.teal -d betanetdata -o htlc.txn

-OR-

If the Sender waits until after the time lock expires, they may construct a transaction with their CloseRemainderTo account to sweep the escrow account.

goal clerk send -a 0 -t SENDERACCOUNT -c SENDERACCOUNT --from-program htlc.teal -d betanetdata -o htlc.txn

Check your results:

goal clerk dryrun -t htlc.txn -d betanetdata

Thanks @ryanRfox. I am still a bit confused. :woman_facepalming:

What I am building is https://algoriddle.xyz - It’s a puzzle game on Algorand - I am putting 100 algos in an escrow account and anyone who can solve the puzzle and find the secret key can send it to the account to unlock the escrow account and get the 100 algos.

My goal is for when the program executes and passes (because the sender included the correct passcode in the transaction args), the escrow account automatically sends the account balance to the person who passed in the correct passcode.

How should I go about doing that? This is my current teal program:

arg_0
byte b64 super-secret-hashed-passcode
==

At first I thought I should send CloseRemainderTo to the sender’s address like this:

let txn = {
        "from": recoveredAccount.addr,
        "to": "5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE",
        "fee": params.fee,
        "amount": 0,
        "firstRound": params.lastRound,
        "lastRound": endRound,
        "genesisID": params.genesisID,
        "genesisHash": params.genesishashb64,
        "closeRemainderTo": recoveredAccount.addr
    };

But I get this error when I try to do that:

transaction cannot close account to its sender 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE

So then I thought maybe I just leave off CloseRemainderTo and the escrow account will close out (minus the minimum balance) automatically, but when I send the txn but without CloseRemainderTo:

let txn = {
        "from": recoveredAccount.addr,
        "to": "5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE",
        "fee": params.fee,
        "amount": 0,
        "firstRound": params.lastRound,
        "lastRound": endRound,
        "genesisID": params.genesisID,
        "genesisHash": params.genesishashb64
    };

I get:

TransactionPool.Remember: transaction 3NDA7PIPSG5HLUZCN4R26BGHWAWFDVE7TBA3H7YUZCQYUFMOOVGQ: overspend (account 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE, data {_struct:{} Status:Offline MicroAlgos:{Raw:0} RewardsBase:0 RewardedMicroAlgos:{Raw:0} VoteID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] SelectionID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] VoteFirstValid:0 VoteLastValid:0 VoteKeyDilution:0 AssetParams:map[] Assets:map[]}, tried to spend {1000})

Any thoughts on how I can go about doing this? Would be really fun to run a puzzle competition to find the passcode to unlock an Algorand escrow account. And I think we are close! Just have to figure out how to do the automatic payout to whoever submits the right solution.

Alright, I understand what you are after. The escrow account will hold some algos and be locked by the hash of the answer to the riddle.

Construct a simple hash lock program called riddle.teal :

arg 0
len
int 32
==
arg 0
sha256
byte b64 Q0oWzpC/ICJPtdpY1hPLIZm0E2Ew3ozIGGNbWLg38aQ=
==
&&

Compile the program:

./goal clerk compile riddle.teal

Fund the escrow address:

./goal clerk send -a 200000 -t JO2EV2OTFZFDBSBH3XHW3IGG7DPPWWSJXXV237OAQPLY3CZOZRBWBUYR5U -f YOURFUNDING ACCOUNT -d betanetdata

Check the balance of the escrow (should be 200000 microAlgos):

./goal account balance -a JO2EV2OTFZFDBSBH3XHW3IGG7DPPWWSJXXV237OAQPLY3CZOZRBWBUYR5U -d betanetdata

Now you must publish the riddle.teal program for your players to evaluate. They must validate the source code produces the same escrow account and verify the asset balance.

The player may now solve the riddle for “my-Super-Secret-32-bytes-of-Data” by constructing a transaction to send all the algos to their account and also specify their CloseRemainderTo account:

./goal clerk send -a 200000 --argb64 bXktU3VwZXItU2VjcmV0LTMyLWJ5dGVzLW9mLURhdGE= -t PLAYERACCOUNT --from-program riddle.teal -c PLAYERACCOUNT -d betanetdata

The above will fail with the overspend error you mentioned above. This is because the escrow account must pay 1000 algos to send the transaction to the player. Construct a new transaction and reduce the amount sent to the player by the required fee:

./goal clerk send -a 190000 --argb64 bXktU3VwZXItU2VjcmV0LTMyLWJ5dGVzLW9mLURhdGE= -t PLAYERACCOUNT --from-program riddle.teal -c PLAYERACCOUNT -d betanetdata

The player should now have 190000 microAlgos in their account and the escrow will be empty.

Wow amazing, thank you. How does that last step work? Is there a way to reduce the amount upfront so that the player never has to encounter an error?

Actually, I had a poorly formed command above (since corrected), sorry. Also, the last example can be improved. Above, I had the player supplying the net amount to transfer, but performing that math is not needed because we use the -c or --close-to flag which 1) closes the escrow account and 2) sends all algos to the --to account. So, just fund the escrow account again and then use this command to generate a zero-value send transaction to liquidate the escrow to the player:

./goal clerk send --amount 0 --argb64 bXktU3VwZXItU2VjcmV0LTMyLWJ5dGVzLW9mLURhdGE= --to PLAYERACCOUNT --from-program  riddle.teal --close-to PLAYERACCOUNT --datadir betanetdata
1 Like

Ohhhh your answer made me realize I had the to and from fields flipped. I was sending the 0 amount txn to the escrow account from the sender, but from your above example it looks like it should actually be to the sender from the escrow account.

I flipped those fields (I am using Purestake and the JS sdk because my testnet node is still not fully synced) but am still getting the overspend error. Any idea what I am doing wrong?

let txn = {
        "from": "5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE", //escrow account
        "to": recoveredAccount.addr,
        "fee": params.fee,
        "amount": 0,
        "firstRound": params.lastRound,
        "lastRound": endRound,
        "genesisID": params.genesisID,
        "genesisHash": params.genesishashb64
    };

But I get:

TransactionPool.Remember: transaction VSBRNZ66QBIDCAMBCYSTBG3IOHEWY3XPHJKSPEHYGJ44KHWHZLSA: overspend (account 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE, data {_struct:{} Status:Offline MicroAlgos:{Raw:0} RewardsBase:0 RewardedMicroAlgos:{Raw:0} VoteID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] SelectionID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] VoteFirstValid:0 VoteLastValid:0 VoteKeyDilution:0 AssetParams:map[] Assets:map[]}, tried to spend {1000})

If I also include the CloseTransactionTo field in the txn, I get:

let txn = {
        "from": "5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE", // escrow account
        "to": recoveredAccount.addr,
        "fee": params.fee,
        "amount": 0,
        "firstRound": params.lastRound,
        "lastRound": endRound,
        "genesisID": params.genesisID,
        "genesisHash": params.genesishashb64,
        "closeRemainderTo": recoveredAccount.addr
    };

I get:

transaction cannot close account to its sender 3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE

Any ideas what I need to change?

Can you confirm the player account is:

3QESO5Z7CXEA4YRSFV6OC4EMTAOA2SC7CQNCDLHT4XLR4PAJ4EGVMPTPQE

Can you confirm the escrow account is:

5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE

I believe the intention is to close the escrow to the player by revealing the secret. I did not find a balance in the escrow account 5BQ3Q...

goal account balance -a 5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE -d betanetdata

The escrow account likely needs to be funded before it may be closed (I’m new to this as well, so not sure about that error). Try sending the minimum microAlgos to the escrow account and repeat your solve riddle transaction.

Confirmed! It looks like there is balance in the escrow account: https://testnet.algoexplorer.io/address/5BQ3QZSMGJ5SG7MND7XPVNTJW6VOETQHXVX7B2QLEXLBDPA6AURGPHYJAE

Ah, I see you are on testnet and I was looking on betanet :joy:

In that case, I’m not sure why is would provide that error.

Ha, ok thanks @ryanRfox. Still appreciate your help!