Teal programming

I tried to write a small teal program that accepts one argument that if you cube it and it equals 8, pays you out any balance that’s in the escrow contract.

This for sure isn’t right – when I compiled it, I got the error * arg 0 wanted type uint64 got []byte

This is the program – is this sort of on the right track?

// in pseudocode, i want:
// function(a) { if (a^3 == 8) { CloseRemainderTo callerOfFunction } }

arg 0

*

arg 0

*

arg 0

==

int 8

// if a * a * a == 8 send txn to address caller

&&

txn Fee

int 1000000

txn CloseRemainderTo

Sender
indent preformatted text by 4 spaces

Your ordering is off. So if you want to cube and check it should look something like:
//note this has no error checking and protection. If you want to use it, please make sure to add //appropriate checks for fees, closeremainder, and round ranges
int 3
int 3
*
int 3
*
int 27
==

Escrow Contract Account
You can compile it something like:
goal clerk compile cube.teal

This will give you an address that you can fund using betanet dispenser:
https://bank.betanet.algodev.network/

you can then write out a transaction to a file to test it like:
clerk send -F cube.teal -t BVTBSJRACDKBEBPQAEVBVYKG4DDAFO7YTQWPF4ND7SIKGZDLKNG4RAJC4U -a 10000 -o cube.stxn -d ~/node/betanetdata

note that if you drop the -o parameter it will send to the network. With the o parameter you can see how the TEAL code is evaluated by doing:
goal clerk dryrun -d ~/node/betanetdata -t cube.stxn
outputs:
tx[0] cost=8 trace:
1 intcblock =>
5 intc_0 => 3 0x3
6 intc_0 => 3 0x3
7 * => 9 0x9
8 intc_0 => 3 0x3
9 * => 27 0x1b
10 intc_1 => 27 0x1b
11 == => 1 0x1

Thank you!!

Really helpful.

You could also enter like:

int 3
dup
dup
*
*
int 27
==

Here is where I am at now – I have this program that will take in three variables a b and c and if a^3 + b^3 + c^3 == 42 then it will send the account balance to the address that called the program correctly.

Here is the teal program:

// in pseudocode, i want:
// function(a, b, c) { if (a^3 * b^3 * c^3 == 42) { CloseRemainderTo callerOfFunction } }

// a cubed
int 80538738812075974
int 80538738812075974
*
int 80538738812075974
*
// b cubed
int 12602123297335631
int 12602123297335631
*
int 12602123297335631
*
// c cubed
int 80435758145817515
int 80435758145817515
*
int 80435758145817515
*
// c cubed + b cubed - a cubed (since a is a negative number needs to be subtracted not added)
intc_0
intc_1
+
intc_2
-
// does it equal 42, if yes send account balance to sender
==
int 42

When I run it, I get a valid transaction at the end but no funds are released from the escrow contract to my address, does that mean my program is returning false?

This is the transaction when I call the function:
https://betanet.algoexplorer.io/tx/335IXYIKUPEXIGTJEDVMSQKJKSU32XZAAD7M2ALV7PLH2PNLSCGQ

This address is the escrow contract – it still has a full balance, it never pays out:
https://betanet.algoexplorer.io/address/ZGCPT5FBQLSYO7A22PWFOJ7VFRNN6AXLWOCO6MYQXBCJHGHF7CYO6ITZTI

Any suggestions on what should be my next step?

I also ran it with smaller numbers to simplify while we debug. This one should be if 2^3 - 1^3 - 1^3 == 6 then send the escrow balance to the sender.

cube.teal:

index.js:

const algosdk = require('algosdk');
require('dotenv').config()


const token = { 'X-API-Key': process.env.PURESTAKE_API_KEY, 'Content-Type': 'application/x-binary' }
const server = "https://betanet-algorand.api.purestake.io/ps1"
const port = ""

var recoveredAccount = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC);
console.log(recoveredAccount.addr);

let algodclient = new algosdk.Algod(token, server, port);
(async() => {
    let params = await algodclient.getTransactionParams();
    let endRound = params.lastRound + parseInt(1000);
    let fee = await algodclient.suggestedFee();

    let program = new Uint8Array(Buffer.from("ASADAQIGIiILIgsiIgsiCyMjCyMLIiMJJAkSJA==", "base64"));

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

    let rawSignedTxn = algosdk.signTransaction(txn, recoveredAccount.sk);

    let tx = (await algodclient.sendRawTransaction(rawSignedTxn.blob));
    console.log("Transaction : " + tx.txId);

})().catch(e => {
    console.log('error', e);
});

txn after running index.js:
https://betanet.algoexplorer.io/tx/CL7P2KQAMVIZU7TEYMNQM7BFGEUWX6624EYBYQVSZBWV26JI52GA

escrow account:
https://betanet.algoexplorer.io/address/VH3FY2SRG3347JN7T4E6OKMPHZYME2NT4PXXOYDQ3LK6W6E45WZDABMB6U

Is that JS code suppose to be funding the escrow or paying out of the escrow?
Also did you run the teal program through a dryrun call?

I used the betabank to fund the escrow.

The JS code is supposed to be calling the function - is there a different transaction I need to send to invoke the program?

you need to create an lsig and also use signLogicSigTransaction function like:

LogicsigSignature lsig = new LogicsigSignature(program, null);

System.out.println("Escrow address: " + lsig.toAddress().toString());

Transaction tx = new Transaction(lsig.toAddress(), new Address(DEST_ADDR), BigInteger.valueOf(1000), amount, firstRound, lastRound, genId, genesisHash);

// send the transaction to the network
try {
    // if this was a delegation operation
    // the lsig object would contian the signature
    SignedTransaction stx = Account.signLogicsigTransaction(lsig, tx);
    // Msgpack encode the signed transaction
    byte[] encodedTxBytes = Encoder.encodeToMsgPack(stx);
    TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
    System.out.println("Successfully sent tx with id: " + id);
} catch (ApiException e) {
    // This is generally expected, but should give us an informative error message.
    System.err.println("Exception when calling algod#rawTransaction: " + e.getResponseBody());
}

Ohhhh – I thought for an escrow contract I needed to comment out the lsig bits. Let me try that.

I pulled this function name from the docs - is this not right?
let lsig = algosdk.makeLogicSig(program)
TypeError: algosdk.makeLogicSig is not a function

javascript file currently looks like this - it’s pulled from the example with the logic sig lines pulled back in:

const algosdk = require('algosdk');
require('dotenv').config()

//Retrieve the token, server and port values for your installation in the algod.net
//and algod.token files within the data directory
const token = { 'X-API-Key': process.env.PURESTAKE_API_KEY, 'Content-Type': 'application/x-binary' }
const server = "https://betanet-algorand.api.purestake.io/ps1"
const port = ""

//Recover the account
var recoveredAccount = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC);
console.log(recoveredAccount.addr);
//check to see if account is valid
var isValid = algosdk.isValidAddress(recoveredAccount.addr);
console.log("Is this a valid address: " + isValid);


//instantiate the algod wrapper
let algodclient = new algosdk.Algod(token, server, port);
//submit the transaction
(async() => {
    //Get the relevant params from the algod
    let params = await algodclient.getTransactionParams();
    let endRound = params.lastRound + parseInt(1000);
    let fee = await algodclient.suggestedFee();

    // move the TEAL  program into Uint8Array
    let program = new Uint8Array(Buffer.from("ASADAQIGIiILIgsiIgsiCyMjCyMLIiMJJAkSJA==", "base64"));

    // makeLogicSig method takes the program and parameters
    // in this example we have no parameters
    // If we did have parameters you would add them like
    // let args = [
    //    Uint8Array.from("123"),
    //    Uint8Array.from("456")
    // ];
    // And remember TEAL parameters are order specific
    let lsig = algosdk.makeLogicSig(program);
    console.log("Escrow address: " + lsig.toAddress().toString());

    // sign the logic with your accounts secret
    // key. This is essentially giving your
    // key authority to anyone with the lsig
    // and if the logic returns true
    // exercise extreme care
    // If this were an escrow account usage
    // you would not do this sign operation
    // lsig.sign(recoveredAccount.sk);

    // At this point you can save the lsig off and share
    // as your delegated signature.
    // The LogicSig class supports serialization and
    // provides the lsig.toByte and fromByte methods
    // to easily convert for file saving and
    // reconstituting and LogicSig object

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

    // 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));
    console.log("Transaction : " + tx.txId);

})().catch(e => {
    console.log('error', e);
});

Did you install with npm? If so it has not been updated yet but will be in the next day or two.

Yep, exactly.

Will reinstall in a few days then. Thanks Jason!

You can always replace the code in the modules dir with with code from github as well.

1 Like

Thanks Jason for all your help so far!

Here is where we are at - would be very cool to get it all working today so we can launch it potentially Monday to celebrate the mainnet launch:

I am using this script to invoke the teal escrow contract program:

This is the script

const algosdk = require('algosdk');
require('dotenv').config()

//Retrieve the token, server and port values for your installation in the algod.net
//and algod.token files within the data directory
const token = { 'X-API-Key': process.env.PURESTAKE_API_KEY, 'Content-Type': 'application/x-binary' }
const server = "https://betanet-algorand.api.purestake.io/ps1"
const port = ""

//Recover the account
var recoveredAccount = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC);
console.log(recoveredAccount.addr);
//check to see if account is valid
var isValid = algosdk.isValidAddress(recoveredAccount.addr);
console.log("Is this a valid address: " + isValid);


//instantiate the algod wrapper
let algodclient = new algosdk.Algod(token, server, port);
//submit the transaction
(async() => {
    //Get the relevant params from the algod
    let params = await algodclient.getTransactionParams();
    let endRound = params.lastRound + parseInt(1000);
    let fee = await algodclient.suggestedFee();

    // move the TEAL  program into Uint8Array
    let program = new Uint8Array(Buffer.from("ASADAQIGIiILIgsiIgsiCyMjCyMLIiMJJAkSJA==", "base64"));

    // makeLogicSig method takes the program and parameters
    // in this example we have no parameters
    // If we did have parameters you would add them like
    // let args = [
    //    Uint8Array.from("123"),
    //    Uint8Array.from("456")
    // ];
    // And remember TEAL parameters are order specific
    let lsig = algosdk.makeLogicSig(program);

    // sign the logic with your accounts secret
    // key. This is essentially giving your
    // key authority to anyone with the lsig
    // and if the logic returns true
    // exercise extreme care
    // If this were an escrow account usage
    // you would not do this sign operation
    // lsig.sign(recoveredAccount.sk);

    // At this point you can save the lsig off and share
    // as your delegated signature.
    // The LogicSig class supports serialization and
    // provides the lsig.toByte and fromByte methods
    // to easily convert for file saving and
    // reconstituting and LogicSig object

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

    // 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));
    console.log("Transaction : " + tx.txId);

})().catch(e => {
    console.log('error', e);
});

The teal program is:

// in pseudocode, i want:
// function(a, b, c) { if (a^3 * b^3 * c^3 == 42) { CloseRemainderTo callerOfFunction } }

// a cubed
int 1
int 1
*
int 1
*
// b cubed
int 1
int 1
*
int 1
*
// c cubed
int 2
int 2
*
int 2
*
// c cubed + b cubed - a cubed (since a is a negative number needs to be subtracted not added)
intc_0
intc_1
-
intc_2
-
// does it equal 42, if yes send txn to sender
==
int 6

And after I run the javascript, the transaction on betanet is:
TMMANDAD36TXUJA5UOEZHVFVVL3KZVHLCCHXYVQGFFN4LFULBJ6A

And I expected it to output true and then send the fund balance to the sender’s address, but no funds are sent from the escrow contract to the sender.

This isn’t directly related to your questions, but some extra TEAL docs just got merged into master on GitHub, maybe they can help: https://github.com/algorand/go-algorand/tree/master/tools/teal/templates/docs

Thank you! Taking a look.

I tried to just make a simple contract that adds one and two and if that equals three send the account balance to the sender. But after I call it, there is still a balance in the escrow contract. What am I missing?

int 1
int 2
+
==
int 3
&&
txn CloseRemainderTo
txn Sender

When I run this program which I expected to just pay out to the sender:

txn CloseRemainderTo
txn Sender

I get this error:
Error: address seems to be malformed

So that must not be the right way to specify to send remainder balance to the sender

you should be able to test that close remainder == sender like something:

txn CloseRemainderTo
txn Sender
==

or

txn CloseRemainderTo
ZeroAddress
==

to verify its not set.

1 Like