Skip to content

ARC-84: ASA-Compatible Smart Contract Tokens #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
backwards compat and ref impls
  • Loading branch information
joe-p committed May 30, 2025
commit 0f9068c0e2975d3f5c52347f77a3f79ce9fb9bc0
95 changes: 91 additions & 4 deletions ARCs/arc-0084.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
Algorand Standard Assets (ASAs) offer trusted accounting enforced by the protocol and stable endpoints for transferring assets and reading data via APIs. ASAs, however, do not offer flexibility. Programmability is desirable for various asset-related functionalities such as enforced fees, automatic royalties, balance snapshots, and conditional transfers. There are also some additional challenges and limitations with ASAs, including:

* Developer and user friction with ASA opt-ins
* Immutable metadata fields (and hacky workarounds like ARC19)

Check failure on line 23 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 23 | * Immutable metadata fields (and hacky workarounds like ARC19) | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`
* Metadata standards (i.e. ARC3) use off-chain mechanisms, which make metadata inaccessible in smart contracts

Check failure on line 24 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 24 | * Metadata standards (i.e. ARC3) use off-chain mechanisms, which make metadata inaccessible in smart contracts | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

As such, there have been various smart-contract-based token standards but they typically have one or more of the following shortcomings:

Expand All @@ -30,9 +30,9 @@
* Lack of existing API compatibility
* Difficulty with indexing token balances and transactions

## Architecture

Check failure on line 33 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

body has extra section(s)

error[markdown-order-section]: body has extra section(s) --> ARCs/arc-0084.md | 33 | ## Architecture |

The interfaces for ARC84 **MAY** be split across multiple apps. The singleton implementation proposed in this ARC uses two apps: The **Data App** and the **Transfer App**. A third optional app in this implementation called the **Transfer Hook App**. Transfers are initiated by calling a method on the **Transfer App**, with all data being stored on the **Data App**, which also does all the accounting. The **Transfer Hook App** **MAY** be used to attach additional logic to transfers and conditionally reject them.

Check failure on line 35 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 35 | The interfaces for ARC84 **MAY** be split across multiple apps. The singleton implementation proposed in this ARC uses two apps: The **Data App** and the **Transfer App**. A third optional app in this implementation called the **Transfer Hook App**. Transfers are initiated by calling a method on the **Transfer App**, with all data being stored on the **Data App**, which also does all the accounting. The **Transfer Hook App** **MAY** be used to attach additional logic to transfers and conditionally reject them. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

### The Apps

Expand Down Expand Up @@ -87,13 +87,13 @@

### Core Philosophy: Separate Representations of Same Asset

A core philosophy of ARC84 is that it is acceptable, if not preferable, to have different representations of the same asset for different contexts. In the many conversations regarding asset standards or ASA changes the developer ecosystem has had, we have tried to make a substantial change without breaking anything or having 100% transparent compatibility with ASAs. This goal has seemed to be impossible so far, so ARC84 embraces the fact that there may be different representations of a given asset and attempts to have seamless conversions to ASAs, but not 100% compatibility. This is why ARC84 uses `uint64` over `uint256` and enables compatibility with off-chain clients through parameters and metadata. The parameters of ARC84 tokens also allow them to be easily bridged to ASAs. While ARC84 tokens won't be directly compatible with existing smart contracts, we can provide the mechanisms for frontends to allow users to bridge between ARC84 and ASAs seamlessly.

Check failure on line 90 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 90 | A core philosophy of ARC84 is that it is acceptable, if not preferable, to have different representations of the same asset for different contexts. In the many conversations regarding asset standards or ASA changes the developer ecosystem has had, we have tried to make a substantial change without breaking anything or having 100% transparent compatibility with ASAs. This goal has seemed to be impossible so far, so ARC84 embraces the fact that there may be different representations of a given asset and attempts to have seamless conversions to ASAs, but not 100% compatibility. This is why ARC84 uses `uint64` over `uint256` and enables compatibility with off-chain clients through parameters and metadata. The parameters of ARC84 tokens also allow them to be easily bridged to ASAs. While ARC84 tokens won't be directly compatible with existing smart contracts, we can provide the mechanisms for frontends to allow users to bridge between ARC84 and ASAs seamlessly. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

Check failure on line 90 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 90 | A core philosophy of ARC84 is that it is acceptable, if not preferable, to have different representations of the same asset for different contexts. In the many conversations regarding asset standards or ASA changes the developer ecosystem has had, we have tried to make a substantial change without breaking anything or having 100% transparent compatibility with ASAs. This goal has seemed to be impossible so far, so ARC84 embraces the fact that there may be different representations of a given asset and attempts to have seamless conversions to ASAs, but not 100% compatibility. This is why ARC84 uses `uint64` over `uint256` and enables compatibility with off-chain clients through parameters and metadata. The parameters of ARC84 tokens also allow them to be easily bridged to ASAs. While ARC84 tokens won't be directly compatible with existing smart contracts, we can provide the mechanisms for frontends to allow users to bridge between ARC84 and ASAs seamlessly. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

For example, a user may want to have an ARC84 representation of USDC that allows for useful features like automatic payments for a subscription service. This user may also want to use USDC in long-standing DeFi protocols and those protocols want to use ASAs due to their accounting guarantees. If they go to a DeFi frontend but only hold ARC84 USDC, they should still be able to swap to the ASA within the same transaction group. Using approvals, this could even be done without any extra transactions for the user. This UX is possible with ARC84, and as shown in the reference implementation, it is fairly low effort for dApp frontends to support this automatic bridging.

Check failure on line 92 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 92 | For example, a user may want to have an ARC84 representation of USDC that allows for useful features like automatic payments for a subscription service. This user may also want to use USDC in long-standing DeFi protocols and those protocols want to use ASAs due to their accounting guarantees. If they go to a DeFi frontend but only hold ARC84 USDC, they should still be able to swap to the ASA within the same transaction group. Using approvals, this could even be done without any extra transactions for the user. This UX is possible with ARC84, and as shown in the reference implementation, it is fairly low effort for dApp frontends to support this automatic bridging. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

### Singleton Data Contract With Transfer Hook: Trusted Accounting Logic

One of the main goals of ARC84 is to find a balance between trust and flexibility. With other smart contract tokens, you cannot have programmability and trust at the same time without building up a list of approved apps and/or programs (which then limits flexibility). ARC84 allows singleton apps to implement transfer and data logic which only need to verified once by the community. Afterward, they can be trusted indefinitely due to the immutability of the contracts. To still allow programmability within this model, an additional app, the **Transfer Hook App**, can implement additional logic to approve or deny transactions, but it cannot break the accounting rules laid out in the singleton. By default the **Transfer Hook App**, cannot even perform transfers out-of-the-box, but automatic transfers from the transfer hook app are possible via the allowance system.

Check failure on line 96 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 96 | One of the main goals of ARC84 is to find a balance between trust and flexibility. With other smart contract tokens, you cannot have programmability and trust at the same time without building up a list of approved apps and/or programs (which then limits flexibility). ARC84 allows singleton apps to implement transfer and data logic which only need to verified once by the community. Afterward, they can be trusted indefinitely due to the immutability of the contracts. To still allow programmability within this model, an additional app, the **Transfer Hook App**, can implement additional logic to approve or deny transactions, but it cannot break the accounting rules laid out in the singleton. By default the **Transfer Hook App**, cannot even perform transfers out-of-the-box, but automatic transfers from the transfer hook app are possible via the allowance system. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

### Separation of Transfer and Data Apps: Data Access In Transfer Hooks

Expand All @@ -105,37 +105,124 @@

### Allowances With Expiration & Cooldown: Safely Enable Delegation Features

Approvals are a key feature of other smart contract tokens, such as ERC20 and ARC200, but they have serious security implications. For example, a large amount of EVM exploits occur due to users giving protocols token approvals and that same protocol being exploited years later after they no longer us the protocol. This is the driving rationale for having an expiration time for allowances in ARC84. By setting an expiration time, users can be sure a protocol will not have access to their tokens when they are done using the app without having to take any explicit action. Similarly, the cooldown mechanism enables a highly-requested feature, subscriptions, in a secure manner. By being supported directly in ARC84, there is no implementation ambiguity and all apps in the ecosystem can clearly display allowance information to users.

Check failure on line 108 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 108 | Approvals are a key feature of other smart contract tokens, such as ERC20 and ARC200, but they have serious security implications. For example, a large amount of EVM exploits occur due to users giving protocols token approvals and that same protocol being exploited years later after they no longer us the protocol. This is the driving rationale for having an expiration time for allowances in ARC84. By setting an expiration time, users can be sure a protocol will not have access to their tokens when they are done using the app without having to take any explicit action. Similarly, the cooldown mechanism enables a highly-requested feature, subscriptions, in a secure manner. By being supported directly in ARC84, there is no implementation ambiguity and all apps in the ecosystem can clearly display allowance information to users. | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

### Collections: On-Chain Guarantees for Valuable Collection Data

### App IDs to Identify Tokens: Ensures no ASA ID collision

Each ARC84 token is identified within its singleton data app via a `uint64` ID (and the combination of data app ID and token ID identify a token for the entire network). The implementation in this ARC uses application IDs to assign token IDs, rather than having a counter that increments each token mint. This is done to ensure that token IDs do not collide with ASA IDs, which enables backwards compatibility features further discussed in the next section

Check failure on line 112 in ARCs/arc-0084.md

View workflow job for this annotation

GitHub Actions / ARC Walidator

proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`)

error[markdown-re-arc-dash]: proposals must be referenced with the form `ARC-N` (not `ARCN` or `ARC N`) --> ARCs/arc-0084.md | 112 | Each ARC84 token is identified within its singleton data app via a `uint64` ID (and the combination of data app ID and token ID identify a token for the entire network). The implementation in this ARC uses application IDs to assign token IDs, rather than having a counter that increments each token mint. This is done to ensure that token IDs do not collide with ASA IDs, which enables backwards compatibility features further discussed in the next section | = info: the pattern in question: `(?i)ARC[\s]*[0-9]+`

### uint64 Accounting: Ensure Compatibility with ASAs

Smart contract token standards on Algorand have to choose between `uint64` and `uint256` for supply and balance arithmetic. Past ARCs, such as ARC200, have chosen `uint256` to align with EVM standards. This allows for a better bridging experience with non-AVM chains that use `uint256`, but hurts the compatibility with the current ASA-based ecosystem. This ARC prioritizes compatibility with the AVM ecosystem, thus uses `uint64` over `uint256`. Compatibility with non-AVM chains should be tackled as a separate problem.

### Collections: On-Chain Guarantees for Valuable Collection Data

The collection an NFT is in contributes to the value of the NFT, so it seems to make sense to include some guarantees about that collection on-chain. ARC84 allows collections to have guaranteed immutability for metadata, a mint cap for tokens in a collection, and a manager address to control who can mint tokens in the collection and modify metadata. These primitive functionalities can be extended to offer as much if not more features than what is currently available through other ARCs such as ARC53.

## Backwards Compatibility

### Bridging ASAs
### Contract Logic: Bridging ASAs

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this bridging in a bit more detail? How does bridging to ASAs work if the asset contract uses hooks to enforce royalties/taxes/etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there will be one canonical bridge, the transfer hook app that checks/automates royalties would need to add an exception when sending form the bridge. They could be taxed going in, but not going out. Anything that doesn't account for this won't be able to be bridged out back to an ARC84. We could actually solve this by allowing the person bridging to ARC84 specify extra txns that need to take place.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. So if the taxes are only enforced when bridging but the ASA is then freely tradable doesn't that encourage users to use the ASA version instead of the ARC84 version? I'm struggling to understand how you can have both an ASA & ARC84 of the same asset and still enforce any of the features of the ARC84.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah for the case of royalties this seems like an unavoidable problem with any solution. It's a problem for any sort of programmable asset, including ASAs. Someone could make a 1:1 USDC bridge without a freeze address, but then you run the risk of Circle freezing everything in the bridge. Presumably that risk would be reflected in the market (in terms of price and where its accepted).

I think most of the benefits of ARC84 are ultimately benefits for the end user. The one exception is the royalty enforcement, but I don't think it's possible to get a perfect implementation of this regardless of ARC84, bridging, or anything else. Even the perfect on-chain solution is susceptible things like trading app control (asset held by app and instead of axfer you change who can control the app), selling rekeys, etc.


ARC84 tokens themselves are not intended to be compatible with existing ASA-based smart contract logic, but they are designed to be easily transferable to an ASA. This allows end users to use their assets with existing apps with minimal friction, regardless if they hold ASAs or ARC84 tokens. To facilitate this user experience, there needs to be an on-chain bridge that can convert ASAs to ARC84 tokens and vice-versa. The specifics of bridging is not part of this ARC, but a token that can easily be bridged is one of the main goals of this ARC. A non-normative reference implementation for a bridge, along with tests, can be found in the reference implementation section.

#### Auto Bridging

Bridging ARC84 tokens to ASAs, should not only be possible, but it should be as seamless as possible for end users. They should not have to think how their assets are represented on-chain before using them. It should be easy for existing apps to integrate "auto bridge" functionality to their client-side code that automatically convers assets for users as needed. Much like bridging itself, this is a core design goal for this ARC. A non-normative reference implementation for auto-bridging can also be found in the reference implementation section.

### ASA API Compatibility

While ARC84 tokens can't have direct compatibility with smart contract logic, it is possible to have direct compatibility with off-chain API endpoints for reading token data. The primary design feature that enables this is the fact that token IDs are unique uint64 IDs that cannot collide with ASA IDs (in the singleton implementation this ARC proposes). This means when one hits an ASA endpoint and the uint64 provided is not an asset, the API provided can fallback to checking the ARC84 token without any additional input needed from the caller.

#### Params & Metadata

The parameters defined for ARC84 align with ASA parameters:

```typescript
export type Params = {
/** The descriptive name of the token */
name: bytes<32>;
/** The short ticker symbol of the token */
symbol: bytes<8>;
/** The total supply */
total: uint64;
/** The number of digits to use after the decimal point when displaying the asset */
decimals: uint64;
/** The address that can modify the token metadata */
manager: Address;
/** The address of the app that implements the transfer hook. If this is not set, transfers are always approved */
transferHookApp: AppID;
};
```

It should be noted that the RBAC fields, such as `FreezeAddress` and `ClawbackAddress`, are NOT in ARC84 as these features can be replicated by the transfer hook and allowance systems.

#### Opt-In & Holding Discoverability

A nice feature of ASAs for clients is that with a single API call you can get all the assets an address holds due to the opt-in mechanism. ARC84 does not have a native opt-in mechanism, but an external singleton contract can be used as a registry for declaring holdings that wallets, explorers, and other apps can use. A non-normative reference implementation for this kind of app is in the reference implementation section. This pattern is not only useful for ARC84, but any smart contract token where app IDs identity a single token.

### ASA Transfer Compatibility

Much like read-only ASA APIs, ARC84 is designed to be compatible with ASA transfers. If a client, such as a wallet, attempts to send an `axfer` and the asset ID is an ARC84 ID, the `axfer` can be translated to a `arc84_transfer` call. `sender`, `assetReceiver`, `assetSender` (with appropriate allowances), and `assetAmount` all translate directly to the `arc84_transfer` parameters. `closeAssetTo` is not a feature of ARC84, but can be replicated with a second atomic transfer in the same `arc84_transfer` call.

Other ASA transactions, such as `acfg` or `afrz` don't have a direct translation, but this is deemed acceptable because these are actions that are often taken in the context of an application (and not from a general client like a wallet).

## Test Cases
Test cases for an implementation are mandatory for ARCs that are affecting consensus changes. If the test suite is too large to reasonably be included inline, then consider adding it as one or more files in `../assets/arc-####/`.

## Reference Implementation
An optional section that contains a reference/example implementation that people can use to assist in understanding or implementing this specification. If the implementation is too large to reasonably be included inline, then consider adding it as one or more files in `../assets/arc-####/`.

### ARC84 Data App

Data app with params, metadata, balances, and allowances stored in box storage. Implements all ARC84 interfaces except `arc84_transfer`.

[TEALScript Source Code](https://github.com/joe-p/arc84_poc/tree/main/projects/arc84_poc/contracts/ARC84Data.algo.ts)

### ARC84 Transfer App

Transfer app that implements `arc84_transfer`. Assets the transfer hook app declared by the token in the data app returns true (if defined).

[TEALScript Source Code](https://github.com/joe-p/arc84_poc/tree/main/projects/arc84_poc/contracts/ARC84Transfer.algo.ts)

### ARC84 Bridge

Bridge between ARC84 tokens and ASAs.

TODO: Also bridge ASAs

[TEALScript Source Code](https://github.com/joe-p/arc84_poc/tree/main/projects/arc84_poc/contracts/ARC84Bridge.algo.ts)

### Auto Bridging: Tinyman Swap

A demo showcasing how these few lines of code can add automatic ARC84 to ASA bridging to perform a Tinyman swap

```typescript
export async function monkeyPatchTinymanV2Swap(algod: algosdk.Algodv2, bridgeAppId: bigint) {
Swap.v2.generateTxns = async (params: GenerateSwapTxnsParams): Promise<SignerTransaction[]> => {
const origGroup = await origSwapTxns(params)

// autoArc84ToAsa is provided by a generic ARC84 library
const autoTxns = await autoArc84ToAsa(
origGroup.map((t) => t.txn),
algod,
bridgeAppId,
)

return autoTxns.map((t) => {
return { txn: t, signers: origGroup[0].signers }
})
}
}
```

This reference implementation is done in TypeScript, which supports monkey-patching. The same concept, however, could be applied to any client written in any language. The core idea is that we take the transaction group before it is sent to users and check if they have enough ASA balance. If not, then check if they have ARC84 tokens and do the swap if available.

[TypeScript Source Code](https://github.com/joe-p/arc84_poc/tree/main/projects/demo/scripts/patched_swap.ts)

### Declaration Registry

Contract used to declare smart contract-based tokens, such as ARC84, providing similar benefits to opt-in for clients reading chain data.

[TEALScript Source Code](https://github.com/joe-p/arc84_poc/tree/main/projects/arc84_poc/contracts/DeclarationRegistry.algo.ts)

## Security Considerations
All ARCs must contain a section that discusses the security implications/considerations relevant to the proposed change. Include information that might be important for security discussions, surfaces risks and can be used throughout the life cycle of the proposal. E.g. include security-relevant design decisions, concerns, important discussions, implementation-specific guidance and pitfalls, an outline of threats and risks and how they are being addressed. ARC submissions missing the "Security Considerations" section will be rejected. An ARC cannot proceed to status "Final" without a Security Considerations discussion deemed sufficient by the reviewers.
Expand Down
Loading