ASA Dispenser / Vending Machine using Smart Contract?

Is it possible to create a single smart contract to which users can send ALGOs and receive a number of ASA tokens based on a pre-determined exchange rate? Looks like the limit order example in the docs can do something like this but it seems like the contract needs to be created for each user. Is there a way to simply have one smart contract where anyone can send ALGOs and get some ASA tokens back? Like a vending machine.

2 Likes

Yes, you can do it using a Stateless ASC1 that handles the following usage:

  1. Opt-In an ASA and fund the ASC1 with that ASA;
  2. Swap ALGO with ASA;
  3. ASC1 creator can collects ALGO funds;
#pragma version 2
// IF: Single AssetTransfer THEN: Handle Opt-In
global GroupSize
int 1
==
txn TypeEnum
int axfer
==
&&
bnz branch_optin
// IF: Single PaymentTransaction THEN: Handle Withdraw
global GroupSize
int 1
==
txn TypeEnum
int pay
==
&&
bnz branch_withdraw
// IF: Group PaymentTransaction + AssetTransfer THEN: Handle Swap
global GroupSize
int 2
==
gtxn 0 TypeEnum
int pay
==
&&
gtxn 1 TypeEnum
int axfer
==
&&
bnz branch_swap
// ELSE: Fail
int 0
return
branch_optin:
// Opt-In Amount is 0
txn AssetAmount
int 0
==
// Opt-In Asset ID
txn XferAsset
int VAR_TMPL_ASSET_ID
==
&&
// Opt-In as Auto-AssetTransfer
txn Sender
txn AssetReceiver
==
&&
// Opt-In Fee limit
txn Fee
int 1000
<=
&&
// Prevent Asset Clawback
txn AssetSender
global ZeroAddress
==
&&
// Prevent Asset Close-To
txn AssetCloseTo
global ZeroAddress
==
&&
// Prevent Rekey-To
txn RekeyTo
global ZeroAddress
==
&&
// Reject Opt-In after LastValid block
txn LastValid
int VAR_TMPL_OPTIN_EXPIRING_BLOCK
<
&&
b end_program
branch_withdraw:
// Only approve withdrawals executed by VAR_TMPL_WITHDRAWAL_ADDR
txn Receiver
addr VAR_TMPL_WITHDRAWAL_ADDR
==
// Withdrawal Fee limit
txn Fee
int 1000
<=
&&
// Prevent Colse-To
txn CloseRemainderTo
global ZeroAddress
==
&&
// Prevent Rekey-To
txn RekeyTo
global ZeroAddress
==
&&
b end_program
branch_swap:
// Assert Swap ratio
gtxn 0 Amount
gtxn 1 AssetAmount
/
int VAR_TMPL_ALGO_ASA_CONERSION_RATIO
==
// Assert Swap Asset ID
gtxn 1 XferAsset
int VAR_TMPL_ASSET_ID
==
&&
// Assert that Asset sender receives Algo
gtxn 1 Sender
gtxn 0 Receiver
==
&&
// Asset that Algo sender receives Asset
gtxn 1 AssetReceiver
gtxn 0 Sender
==
&&
// Asset Transfer Fee limit
gtxn 1 Fee
int 1000
<=
&&
// Prevent Asset Clawback
gtxn 1 AssetSender
global ZeroAddress
==
&&
// Prevent Asset Close-To
gtxn 1 AssetCloseTo
global ZeroAddress
==
&&
// Prevent Rekey-To
gtxn 1 RekeyTo
global ZeroAddress
==
&&
end_program:

I’ll try to write a little solution on this topic, since this kind of use-case arose more than once.

3 Likes

Thanks so much for this! I don’t quite understand the code at the moment so I’m going to take some time reading through the docs and decoding it.

That being said, is this how it works in general?

Setup:

  1. ASA creator compiles the smart contract code above, which yields a contract address
  2. ASA creator sends ASA tokens to the contract address (opt-in seems to be automatically handled by the contract?). This process can be repeated later as needed.

User Interaction:

  1. To buy ASA tokens with ALGOs, the user creates a group tx consisting of 2 txs:
    i. Send ALGOs from user address to contract address
    ii. Send ASA tokens from contract address to user address
  2. Both txs are signed by the user and submitted to the blockchain.
  3. Assuming all conditions set forth by the contract are met, such as the swap ratio, the atomic swap will be successful.

Creator Interaction:

  1. At any point in time, the ASA creator may withdraw ALGOs from the contract address. (ASA token withdrawals don’t seem to be allowed by the code above, but it can be added right?)

Now I assume most users are not tech-savvy, so we would need some front-end that creates and submits the necessary transactions for the swap to occur. I’d like to avoid that if possible. Is there no way to do this with a single payment transfer to the contract address so users can use existing vanilla clients like the official Algorand Wallet? Or are smart contracts limited to only approving/disapproving transactions explicitly pre-constructed and submitted to them? As in do they not have the ability to create new transactions from within the contract? If not, is this due to a fundamental limitation or a feature that could be added later?

2 Likes

ASA Opt-In is not automatic but is approved by the TEAL logic. Please note that there is a block expiring condition for the Opt-In transaction to prevent that malicious users exploit the Opt-In transaction approval over time just to consume the Contract Account ALGO funds in transaction fees.

So the right approach would be:

./goal clerk compile swap.teal

swap.teal: SWAP_CONTRACT_ADDRESS

Then as any other Account on Algorand, the Contract Account must be initialised with the minimum balance of 0,1 ALGO. For each ASA Opt-In the minimum balance of an Account rises up by 0,1 ALGO. Since your Contact Account must Opt-In the ASA and then pay for the Opt-In transaction fee you may initially fund it just with 0,201 ALGO (or a bit more in case of higher transaction fee).

./goal clerk send -f YOUR_ADDRESS -t SWAP_CONTRACT_ADDRESS -a 201000

Now you can execute the Opt-In:

./goal asset send -f SWAP_CONTRACT_ADDRESS -t SWAP_CONTRACT_ADDRESS --assetid VAR_TMPL_ASSET_ID -a 0 -o optin.txn

./goal clerk sign -i optin.txn -p swap.teal -o optin.stxn

./goal clerk rawsend -f optin.stxn

and fund the Contract Account with an amount of ASA:

./goal asset send -f YOUR_ADDRESS -t SWAP_CONTRACT_ADDRESS --assetid VAR_TMPL_ASSET_ID -a ASA_FUND_AMOUNT

Users can execute the swap Atomic Transfer in this way:

./goal clerk send -f USER_ACCOUNT -t SWAP_CONTRACT_ADDRESS -a ALGO_AMOUNT -o payment.txn

./goal asset send -f SWAP_CONTRACT_ADDRESS -t USER_ACCOUNT --assetid VAR_TMPL_ASSET_ID -a ASA_AMOUNT -o asa_transfer.txn

cat payment.txn asa_transfer.txn > swap.txn

./goal clerk group -i swap.txn -o swap.gtxn

./goal clerk split -i swap.gtxn -o unsigned_swap.txn 

./goal clerk sign -i unsigned_swap-0.txn -o swap-0.stxn

./goal clerk sign -i unsigned_swap-1.txn -p swap.teal -o swap-1.stxn

cat swap-0.stxn swap-1.stxn > swap.sgtxn

./goal clerk rawsend -f swap.sgtxn 

Note that another useful condition on “minimum swap” account can be included to avoid user swapping 0 ASA with 0 ALGO consuming the Contract Address ALGO fund dedicated to transaction fees for dummy swaps. Other strategy could requiring Users to pay for swap transaction fees including an additional fee into the ALGO payment transaction.

You can withdraw ALGO from the Contract Account in this way:

./goal clerk send -f SWAP_CONTRACT_ADDRESS -t VAR_TMPL_WITHDRAWAL_ADDR -a ALGO_WITHRAWAL_AMOUNT -o withdrawal.txn

./goal clerk sign -i withdrawal.txn -p swap.teal -o withdrawal.stxn

./goal clerk rawsend -f withdrawal.stxn

ASA withdrawal by VAR_TMPL_WITHDRAWAL_ADDR may be also included as further withdrawal condition into the TEAL logic.

TEAL can note creare transaction, just approving or rejecting them. Algorand approach to Smart Contract is intentionally split in “Transaction Execution Approval” and transaction creation on client side, since the real valuable part to be handled on chain is not the transaction creation but its approval or rejection. So the interaction with the ASC1 must be handled in part on your application back-end/front-end through the Algorand SDKs and in part on-chain thorough TEAL.

I know that @StishSits is working on something similar, maybe you can join him to come up with a solution together.

1 Like

I have been working on building this and it seems easy enough but have not been successful yet. Hoping to get back on this Friday and accomplish a simple ui using algowallet connect which just came out and allows to easily do what we need to do. That way it is cross browser supported. I’ll be happy to share it once done.

Thanks for the tips. I’ll start experimenting with the code @cusma shared above.

@StishSits
Let me know how your integration with MyAlgo Connect goes. I’m going to try AlgoSigner first.

1 Like

I ended up going with the delegated signature route instead of setting up a contract address. Easier to manage for my use case.

Now I need a front end that can create grouped transactions, sign (using their private key and the lsig I provide), and submit. Doesn’t look like there’s any client out there right now that actually let you do this. They all only support creating one transaction. I don’t have much front-end experience and really don’t wanna develop one just for this when it feels like this should really be a generic feature supported on the official wallets.

A sort of public repository of templated transactions would be nice too. Devs can upload transaction templates along with any necessary lsigs. Then any user can come around and create/submit from the templated transactions. Or maybe my app is a special use case where the only front-end functionality I need is precisely this much and nothing more :frowning: … Anyone wanna make an ASA marketplace?

2 Likes

When I first started using AlgoStudio for developing the smart contracts I read a lot about delegated signatures and ran into the same problem of how to create a grouped transaction and then do an atomic transfer. It is hard to come from Solidity to Teal so far for me anyway because a lot of the logic was built into the Ethereum Smart contracts.

I did get to work more with Algosigner and have a pretty cleaned-up framework that works on mainet for Asset Optin. But users can simply add an Asset to their Wallet on any wallet platform for cheap now. I know there are still use cases for this though.

I kinda wish they would get rid of asset Optin and Application Optin and just charge a high enough fee upon creation to prevent spam. It would be a better user experience and developer experience. Someone went out of their way to prevent a problem that may or may not exist and there was a simpler solution to resolve it. I would pay $100 algos maybe more, to not have the user optin to my contracts and asset.

I’m sure a lot of the quick stuff we need is coming. I love the idea of a template marketplace. Right now everything is kind of scattered still or if you didnt know about X then it will not work the way you think it should.

It seems like there would be an easier way to send Algo and receive asset. I’m sure there are way smarter people than me working on these things and as they become available hopefully we can implement it.

For our use case we are trying to build basically an Algorand endowment fund to give new users confidence that the platform can sustain long term until our later stage tokenomics kick in for the SITS asset. Having a little nest egg of Algorand drawing interest that covers operational expenses would definitely allow a user to feel they have less risk of platform failure.

I have even thought about if there was a way that users could delegate some Algorand to us. So we don’t hold it but we receive the interest from it or a portion of interest. That way holders still control their Algo for when the boom comes but we can cover operating costs and expansion costs and user acquisition costs etc. Then we avoid the legal challenges of jurisdictions claiming we are running a Token Event, even though we are not because our Asset will have several utilities. We have to raise funding if we want to reach our goal of getting 10,000 New users on Algorand this year.

(full disclosure: I do hold a small stake in Algo and would benefit greatly by increasing the userbase of Algo. )

1 Like

Thanks for your feedback!

Asset opt-in and application opt-in

Regarding asset opt-in and application opt-in, this is a complex question. One design principle of Algorand is that participation nodes only need to store a very small amount of data, namely the last 1000 blocks and the account table, that contains all application states.
If the account table blows up, this significantly raises the bar for new people to participate.
The minimum balance is there to prevent that: everything you store in the account table (asset balance, application local state, …) raises the minimum balance from 0.1 Algos to something higher.
Since there is a limited number of Algos, this automatically creates an upper bound on the account table size.
If you were just to pay $100 to create a smart contract independently of the number of users of the smart contract, you would automatically lose this property: it would be easy for $100 to spam the blockchain with many useless accounts opting in to your new application, which would blow up the account table.

On the other hand, we could imagine having the creator of the asset/application pay some fee/minimum balance that depends on the number of users of the application.
But there might be two issues to solve:

  1. It may be possible to send assets to people that do not want to receive it. This may create undue tax liability. On the other hand, this issue is already there in Ethereum.
  2. It may be difficult to prevent spam: people joining your applications preventing other users to join it.

Template marketplace

Definitely would be a great addition.

Delegating Algos so that you receive portion of rewards

This is completely possible with the current technology.
One solution is to have the user send their Algos to an escrow contract account and have a stateful application control the escrow contract account.
If you are interested, I can give more details.

2 Likes

Thanks for putting the asset optin rationale into a clear perspective. That makes sense now. I would love to know more about configuring an escrow for allowing users to delegate their Algo interest or a portion of it. This could be a viable way to raise capital or at least prove to people that there is serious interest in a project in order to possibly secure capital from traditional sources. Thanks again for the reply it was very helpful.

If you want to receive all the participation rewards of the users, what you can do is create a stateless smart contract escrow account C and an application (stateful smart contract) App that works as follows:

  • To join, a user will send a group of two transaction:
    1. Send x Algos to C
    2. Opt in to the application App that will record in the user address that they sent x Algos, and also keep in global state the total amount of Algos sent by user (call it Total)
  • C allows any transaction from C to an account you own if it is done in a group transaction with a call to App. And App will in that case allow any such transaction as long as the resulting balance of C is above Total. This allows to get all the participation rewards at any point in time.

To have an example of such a application using an escrow account, see the crowfunding stateful smart contract application.

If you want to only take part of the participation rewards, it becomes a bit more complex.
What you need to do is create one escrow account per user.
You can do it using the ideas in Bond Implementation - #7 by fabrice

2 Likes

Thank you very much for the info and the link. Recently I cut off a finger and have been very slow to recover. I’ll start coding again and being online more hopefully in another week or so. This will get me in the right direction for sure. One reason I love Algorand so much is that everyone is super helpful. Thank you.

Thanks! I hope you get well soon!
Don’t hesitate to ask any further questions.

1 Like

Has anyone been successful in completing this vending machine for ASAs. It is something I am interested in. Here’s what I am looking to do.

A wallet will be loaded with a number of NFTs (this will be the wallets only contents, except for a small amount of Algo’s to cover transaction costs)

On the site, visitor can choose to purchase a randomly chosen NFT for a set fee.

When they click the buy button:
Step 1: My Algo wallet creates a transaction for them to sign
Step: Fee sent from vistor to shop
Step 3: NFT is selected at random.
Step 4: Receiving wallet opts in to accept the ASA ID.
Step 5: The ASA is then transferred
Step 6: A screen reveals the NFT they have received (each NFT will have the image hosted on IPFS)

Other requirements:
Limit of number of purchases for each wallet address, to reduce one person buying all of the NFTs.

Important that the funds and transferred before the ASA id is available to view. We don’t want people to be able to start a transaction, see the NFT that has been selected then not complete so they can try again to receive a different one.