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.
Yes, you can do it using a Stateless ASC1 that handles the following usage:
- Opt-In an ASA and fund the ASC1 with that ASA;
- Swap ALGO with ASA;
- 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.
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?
- ASA creator compiles the smart contract code above, which yields a contract address
- 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.
- 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
- Both txs are signed by the user and submitted to the blockchain.
- Assuming all conditions set forth by the contract are met, such as the swap ratio, the atomic swap will be successful.
- 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?
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.
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.
Let me know how your integration with MyAlgo Connect goes. I’m going to try AlgoSigner first.
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 … Anyone wanna make an ASA marketplace?
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. )
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:
- 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.
- It may be difficult to prevent spam: people joining your applications preventing other users to join it.
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.
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:
- Send x Algos to
- 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
- Send x Algos to
Callows any transaction from
Cto 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
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
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.