CAP-0048 and CAP--049 (Jump Cannon classic interop)

92 views
Skip to first unread message

Nicolas Barry

unread,
May 11, 2022, 9:34:44 PM5/11/22
to Stellar Developers
Spinning up a thread here as most conversations have been happening on Discord.

See

Jon, here are some notes and thoughts I took while reviewing those CAPs.

CAP-0048:

 Invocation model

Maybe just a detail, but we should not need to introduce a new way to have "contractID" (or maybe you meant this to be some extension to what is described in CAP-0047?) - there is a need for core managed contracts, we could just have a special account reserved for "the network" that can be managed like any other core-protocol upgrade.

It may be implemented as a combo WASM + higher privileged host functions (which would allow to share code between "basic" S-ERC20 (Stellar's version of ERC20) reference implementation and this classic interop, or if we have the ability as a "native contract", taking advantage of Rust). This can be modeled as an extension of top of "normal" contracts (from a contract + manifest point of view).

 

Strong dependency on "S-ERC20".

The problem I see here is that we do not have that base contract defined yet, and this CAP doesn't really go in detail on choices being made here (I suspect that S-ERC20 should be its own SEP).

The contract has some weird things in there: uses generic "String" to manage assets seems like a footgun as Strings are not strongly typed (in JS, Solidity's cousin, it makes sense, but we're trying to be strongly typed with Rust as to catch errors at compile time).

It also inherits some of the security issues from ERC20 (see ERC20 API: An Attack Vector on Approve/TransferFrom Methods - Google Docs ) that we should fix/mitigate.

 

Allowances

Why are "allowances" implemented differently than what a "basic" S-ERC20 implementation would do (using custom ledger entries)? This depends on what the "auth model" is for ERC20, but I do not see a reason to "one off" this one (I guess this is related to my question around why not sharing the implementation with smart code, we'll surely have a reference implementation).

 

"NATIVE"

The CAP is very light on how it interfaces with NATIVE, but should be specified.

 

Re: Issue: Issuer Balance

I think this should always be 0:

regardless of fixed supply or not, I suspect that the main use case for using this balance is to exclude it from "total supply" (which is not really available anyways).

 

Re: Issue: Not Compatible With High Supply Assets

I do not think this is true as long as you cannot "mint" more than INT64_MAX at a time (I do not see "mint"/"burn" covered in this CAP but we should just not allow to mint more than that number for classic assets to be 100% compatible if we add a mint method).

When interacting with ERC20 tokens, contracts would have to deal with uint256 total supply, which cannot reasonably be reached in INT64_MAX increments.

 

 

Re: Issue: Classic Payments To Contracts

I do not understand that section. Which destination address a classic payment would use that would correspond to a smart contract? I do not see this covered in this CAP.

Also I think we should *not* allow to burn/mint with payments using that ERC20 interface.

 

 

CAP-0049:

 

Defines "S-ERC20"

same thing than for CAP-0048, we need to discuss this in detail.

 

Defines a "compliance interface".

This one seems kind of odd: the requirements around "compliance" in smart seem very fluid. I do not know if the proposed interface meets any specific requirements on that front (the reason they work in "classic" is that we can leave the ledger in a "compliant state" when auth is revoked for example), for a wrapped asset auth revocation would have a different meaning than its classic counterpart. Also for "clawback" to really work, it's not as simple as adding a "clawback" method: this is clearly not enough to make it work with an AMM for example (you would not blindly clawback from the AMM's reserve).

 

I think we're probably better off iterating outside of the protocol on this aspect.

 

Usability

Having to keep track of both the classic asset and the wrapped one looks like some serious complexity at that layer (which explodes exponentially as we go higher in the stack).

 

 

Compliance

 

There is the larger question on the consequences of auth revocation: in classic "revoke" implies that the revoked account loses control over that token and the protocol works extra hard to also recover liabilities (offers get cancelled for example) so that things like "clawback" works.

 

It seems to me that allowing a blanket "bridge" for classic assets may break compliance and we need to discuss this a bit more with the community.

 

At a minimum, I think we cannot allow to bridge balances that are "clawback enabled" for CAPs (following the precedent from ClaimableBalances).

I *think* that for simple "auth required" assets, things are broken: in today's protocol we cancel offers/pools on auth revocation. If there is a "smart pool", we would not be able to recover the assets locked in that pool. This may be acceptable by the ecosystem.

 

If this is not acceptable, we'll have to also disallow "auth required" assets to move to smart.

 

We could do something more complicated in the future to open things up some more:

Allow issuers to "deploy" their own "bridge override" under their account that implements S-ERC20.

That override could implement additional restrictions, for example, only allow accounts to interop with specific contracts that implement certain compliance interfaces or use a "trust list" of sorts.

This could work well for CAP48 (they would have full access to trustlines for assets issued by them, except for liabilities that are not accessible).

For CAP-0049 it would be similar, they'd have access to the "unwrap" internals.

 

Comparing CAP-0048 and CAP-0049

 

Both require us to commit at the protocol layer to a specific protocol "S-ERC20", which is not necessarily a bad thing to have on day one.

 

Originally I thought that CAP-0049 would be a lot better as it starts with it seems a much narrower interface with classic, but I think a lot of the advantages vanish as we specify things in terms of S-ERC20. Then the type of interactions that we allow on trustlines is small enough that I can even see opening things up to smart contracts entirely.

 

With CAP-0049, we'd have "total supply" still broken as it's only tracking a portion of the assets.


Nicolas


Jonathan Jove

unread,
May 12, 2022, 11:25:00 AM5/12/22
to Nicolas Barry, Stellar Developers
Hi Nicolas,

Thanks for the feedback.

# CAP-0048

## Invocation Model


> Maybe just a detail, but we should not need to introduce a new way to have `contractID` (or maybe you meant this to be some extension to what is described in CAP-0047?)

This is an extension on CAP-0047.


> we could just have a special account reserved for "the network" that can be managed like any other core-protocol upgrade.

I really don't see the advantage of this approach. No matter how you spin it, to achieve good interoperability you need interoperability to provide a widely accepted token standard. That means every token has its own "contract address". Because every token automatically gets an interoperability layer (because we don't want the issuer to need to take any action), how could you assign an integer contractID to those assets? And using the asset itself as an identifier means you always know how to find the relevant interoperability layer rather than performing an additional lookup.


> It may be implemented as a combo WASM + higher privileged host functions (which would allow to share code between "basic" S-ERC20 (Stellar's version of ERC20) reference implementation and this classic interop

If we're going to provide native support, it should have maximum performance. Quoting the requirements section of CAP-48, "Tokens are the basic unit of blockchain applications, and as such they should be very efficient to use." Therefore we should not use any WASM in this.

## Strong dependency on "S-ERC20"


> The problem I see here is that we do not have that base contract defined yet, and this CAP doesn't really go in detail on choices being made here (I suspect that S-ERC20 should be its own SEP).

The primary goal should be to deploy something that developers are already widely familiar with and that has proven to work. The goal of smart contracts on Stellar should be to reduce barriers to innovation on Stellar, not to create new ones. Adopting a widely used standard *as-is* would be a great approach to solving that problem. From my perspective, the burden of proof needed to adopt any other interface is enormous so I don't think it's a good use of time to debate it.


> It also inherits some of the security issues from ERC20 (see ERC20 API: An Attack Vector on Approve/TransferFrom Methods - Google Docs ) that we should fix/mitigate.

I'm aware of this security issue, and I am not concerned about it because there is a simple workaround (clients should always set the allowance to 0, observe it, then update it). We can provide additional functions, if we so choose, that are safer. Indeed, safe allowance functions are common in the Ethereum ecosystem (see https://github.com/OpenZeppelin/openzeppelin-contracts/blob/e633ee9ed3fe45290fb02cdc61eaac3c82b6ba76/contracts/token/ERC20/utils/SafeERC20.sol#L60-L80). But as noted above, I have no interest in using an interface that is not at least compliant with ERC-20.

## Allowances


> Why are "allowances" implemented differently than what a "basic" S-ERC20 implementation would do (using custom ledger entries)?

This was a direct request from Graydon who wants data to be transparent rather than opaque whenever possible. Personally, I would be happy to use contract data entries but I'm trying to produce a proposal that will satisfy the desires of many different people.

## "NATIVE"

Specifying this is busy-work. It works exactly as you expect it to, using accounts instead of trustlines. When we agree on an approach, I will flesh out the details but there are more important things to do first.

## Re: Issue: Not Compatible With High Supply Assets


> I do not think this is true as long as you cannot "mint" more than INT64_MAX at a time

You are missing the point of CAP-0048. The balance always lives in an actual trustline, which cannot exceed INT64_MAX balance. It is trivial to make a contract unable to receive additional funds in this model, as demonstrated in the proposal.

## Re: Issue: Classic Payments To Contracts


> Which destination address a classic payment would use that would correspond to a smart contract?

You are missing the point of CAP-0048. The balance always lives in an actual trustline on an actual account (or in the account itself for native), so of course it would be possible to send money to it directly. For example, `transfer` is defined in terms of `PaymentOp`. I will try to make it more obvious in the proposal that actual trustlines are being used here.

# CAP-0049

## Defines "S-ERC20"

Same response as above.

## Defines a "compliance interface"

I see that I didn't elaborate on the need for this in the proposal, so I will fix that.

The compliance interface is basically a hard requirement of CAP-0049, and I would be strongly opposed to anything like CAP-0049 in the absence of the compliance interface. Imagine that the compliance interface was missing from CAP-0049. Then this is a simple mechanism to avoid authorization:

1. `wrap` your money -- because it isn't in a trustline, there is no way to freeze or clawback
2. `transfer` it to whatever account you want -- it can't be frozen, so you can always do this
3. `unwrap` the money when you want to

To be clear, this is _not_ part of the interface that other S-ERC20 contracts would have to implement to ensure interoperability. It is common in Ethereum for contracts to have extensions beyond the core interfaces they implement. For example, USDC has a "blacklist" functionality which is exactly like what is described in this proposal (in fact, I checked that they would be equivalent when designing the interface).

# Compliance


> There is the larger question on the consequences of auth revocation: in classic "revoke" implies that the revoked account loses control over that token and the protocol works extra hard to also recover liabilities (offers get cancelled for example) so that things like "clawback" works.
>
> It seems to me that allowing a blanket "bridge" for classic assets may break compliance and we need to discuss this a bit more with the community.

Both CAP-0048 and CAP-0049 are designed to preserve compliance controls. In CAP-0048, everything works exactly the same because balances lives in trustlines. In CAP-0049, there is a new compliance interface to handle this.


> At a minimum, I think we cannot allow to bridge balances that are "clawback enabled" for CAPs (following the precedent from ClaimableBalances).
>
> I *think* that for simple "auth required" assets, things are broken: in today's protocol we cancel offers/pools on auth revocation. If there is a "smart pool", we would not be able to recover the assets locked in that pool. This may be acceptable by the ecosystem.

This is wrong, both CAP-0048 and CAP-0049 support this functionality. In CAP-0048, you would simply use clawback on the "smart pool" trustline. In CAP-0049, you would use the `clawback` function on the "smart pool" address. Contracts that would be broken by such an action should not support those assets, or should have explicit guards that check that a balance is equal to the expected value.


> If this is not acceptable, we'll have to also disallow "auth required" assets to move to smart.

This is not reasonable. There are very few (maybe no?) meaningful assets that have `AUTH_IMMUTABLE` set, so they could become `AUTH_REQUIRED` or `AUTH_REVOCABLE` at any time. Then what would you do with the assets that were already transferred? These proposals were carefully crafted to make it possible to support assets with issuer flags set.

Thanks,
Jon

--
You received this message because you are subscribed to the Google Groups "Stellar Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to stellar-dev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/stellar-dev/039866ef-a2bf-454a-abf7-22ea6fe7a3dcn%40googlegroups.com.

Alex Mootz

unread,
May 16, 2022, 12:17:02 PM5/16/22
to Stellar Developers

Hello,

Attempted to focus feedback on usability from the dApp user / developer perspective.

TL;DR

CAP-49 makes sense if the long term goal will be centralized access points (ex. Vibrant) and smart contract assets (ex. Aave's aUSDC) are intended to be created outside of Stellar Classic assets. CAP-48 with totalSupply makes sense if the long term goal is decentralized access points (ex. accessing Aave through an IPFS node) and smart contract assets (ex. Aave's aUSDC) are intended to be created as Stellar Classic assets.

## CAP-48

Lack of TotalSupply()

I think this is a requirement for supporting useful smart contracts. Tracking “shares” makes determining the value of a user's deposit into a smart contract much simpler, and is used in AMMs, lending protocols, and various token vaults.

Uniswap’s V2 “mint” as an example: https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol#L118

Stellar Classic as Primary Control Point

It is likely that a contract will want the ability to “mint” and “burn” tokens. It is unclear to me if this can be supported easily. Would the “contractId” need to match the issuing account of the asset to ensure it has the appropriate signatures to mint new tokens? On that note, would the addition of multi-sig to the issuing account on the classic side break the smart contract? Apologies if I’m just missing something here.

Issue: Available Balance vs Balance

I don’t recall how the liability mechanic works well enough to have a strong opinion. It seems to make the most sense that `token.balanceOf(account)` returns the amount of token that the account has available to spend. However, this reads like the sum of all account balances for the token would be less than the total supply if a liability exists (which is weird). If this is the case, as long as `transfer` and `transferFrom` ensure the liability edge cases are supported, returning the full balance including liabilities would be the clearest.

Usability

Users only having to maintain a single asset for Stellar USDC is a massive plus. Further, this allows dApp UIs to display things like “USDC” and “XLM” to the user, since we can make use of the Stellar Classic asset. Lastly, this allows smart contract native assets (like an aUSDC token from Aave) to take advantage of future things like Speedex.

Another point to note is that smart contract developers will eventually want something more out of the asset object, resulting in new CAPs or weird mixture of smart contract tokens and Stellar Classic tokens. I think a mixture of Stellar Classic assets and "smart contract native" assets should be avoided if possible.

## CAP-49

Issue: Not Compatible With High Supply Assets

Does the Rust environment have support for a native double or floating point number? Might be worth exploring for developer simplicity over the typical `uint` route of today. I know there has been work/discussion to improve fixed point support in Solidity: https://github.com/ethereum/solidity/issues/409

Usability

There are a few outlying things with a “S-ERC20” bridge contract.

Will “smart contract native” tokens be able to be bridged to Stellar Classic? I can’t wrap my head around if this would be a net advantage to make use of things like Speedex / lower cost transactions, or a net negative due to liquidity fragmentation.

UIs will look pretty weird for dApps, since the majority of assets will be wrapped assets from Stellar Classic. Is there any way that users could verify that this `wUSDC` contract is the actual wrapped USDC from Circle’s Stellar USDC instead of something malicious?

> With CAP-0049, we'd have "total supply" still broken as it's only tracking a portion of the assets.

This is a good point, but I don’t think it blocks smart contracts like the totalSuppy of CAP-48

Consider a “smart contract native” asset. The creation/destruction of tokens is controlled in full by the deployed contract, so it always has the correct look of `totalSupply`, regardless of if the tokens can get bridged to Stellar Classic or not. This meets the requirements for building things like AMMs, lending protocols, and token vaults that utilize the token “share” mechanism for tracking user deposit value.

For a bridged asset, the totalSupply would represent the total amount bridged. Smart contract developers just need to be aware that this isn’t the full amount, and UI developers can still get the full amount if Horizon adds a `wrapped_amount` section to the `assets` call.

Best,
Alex

Nicolas Barry

unread,
May 20, 2022, 9:26:56 PM5/20/22
to Stellar Developers

Coming back to this thread now that have a better understanding of some of the challenges in the space (some of the conversations around clawback and CAP-0050 at least helped me even if sometimes it may seem like we're running in circle).

 

I think the priority in terms of interop should be to optimize for innovation.

What do I mean by this?

 

In the short term our version of how to model a "classic" token is going to be quite primitive: an "ERC-20" without much guarantees.

 

As we iterate on token standards with the ecosystem, I think that we're going to find that certain things make a lot more sense than others.

For example:

  • I expect a good level of innovation on the compliance front to make it easier for contract developer to handle scenarios like "clawback" better.
  • Improved token specifications. Working at the ERC-20 level gives maximum reach to contract developers, but is quite challenging and error prone. Better standards frees up contract developers to focus on solving business problems instead of correctness problems.

 

With that said, I think that the approach that we'll take should allow the interop protocol to keep up with the ecosystem instead of slowing it down.

 

Related to this, one of the pattern that I found useful so far in deciding on how to integrate a feature in the core protocol vs not is to first start with a solution that allows for maximum expressiveness and extensibility in client code, and add core protocol features to enable some "high value" scenario so that they become easier/faster (in other words: core level support should be considered as a way to "promote" parts of what can be done in SEPs that reached broad acceptance into CAPs).

 

So, going back to CAP-0048 vs CAP-0049 (and following up on some of the comments from Alex).

 

I also think that the split balance from CAP-0049 is a minor inconvenience (I flagged it as "a major UX issue" previously), because systems like Horizon can just always return the wrapped and unwrapped balances.

As the wrapped/unwrapped asset is a core concept, we can add that concept to the `Asset` type in a similar way that we added "muxed" enum values when we introduced multiplexed account (basically: reserve one bit on the asset code to mean "wrapped").

Allocating part of a given balance to the classic or smart side is still low friction: it's just a matter of executing 1 transaction. If this ends up being a "high frequency" type of scenario, we can always make it faster/more efficient later.

 

I also think (going back to what I wrote earlier), that with CAP-0049 it will be relatively easy to add new capabilities to the Classic-Wrapped token as it is for the most part just like any other token on the network. So if we want to add a better compliance protocol (the one from the CAP is too simplistic), we will be able to do it regardless of where we land with the broader community.

 

CAP-0048 on the other hand is a hard commitment on the interop front. It's tightly coupled with the quirks of the classic protocol, and every time we want to add a new capability we'll have to perform a deep analysis on its consequences with the classic implementation.

 

I think that things get interesting when considering the interop in the other direction (that was not discussed at all so far):

 

How does things look like for tokens that are issued on the smart side, how do they get exposed to classic? I believe that "classic" (or a better version of it), is the future for some highly specialized operations on the network (like payments) so supporting this direction of interop is critical.

 

In a CAP-0049 like world, wrap/unwrap semantics are already there, so I can envision future protocol changes that would just mimic what CAP-0049 does but in reverse (there are some failure modes to consider, but it's basically just that). This can potentially be done with the issuer contract in full control (as classic access control is not granular, this is required). Operations normally granted to the issuer would not be granted on the classic side (as there would not be any way to fulfill them).

 In a CAP-0048 like world, I think it gets kinda weird. There needs to be a classic asset usable.

So the two options are:

  • All assets, including smart assets have to use trustlines. This forces assets into the "int64" model that classic uses. This is incompatible with basically every other chain out there, making working with Stellar contracts very different than anything else out there. Some code becomes very hard to port over. I think this option is a non starter.
  • Smart assets can use whatever they want… but then we need to basically do the equivalent of wrap/unwrap into a classic model to at least track balances (either directly or via some pegging contract). This option looks a lot like CAP-0049, making this option the "worst of both worlds".

 

At this point I think that CAP-0048 (or any variation that builds on top of trustlines), is just not workable without significant engineering effort, and so far I didn't hear any real compelling argument to go this way to justify this (we may just be facing a case of premature optimization).

As a consequence, my recommendation is to focus on CAP-0049 and hash out open issues.

One thing I didn't cover as I do not completely understand it here is Alex's comment on centralized vs decentralized model. Is that in relation to the governance model for tokens? Some allow "free flow" like tokens that are not "auth required" vs ones that require the issuer to be involved in some way.

Cheers

Nicolas

Alex Mootz

unread,
May 26, 2022, 4:44:07 PM5/26/22
to Stellar Developers
Overall, I'm in agreement with Nicolas that, "As a consequence, my recommendation is to focus on CAP-0049 and hash out open issues."

> Improved token specifications. Working at the ERC-20 level gives maximum reach to contract developers, but is quite challenging and error prone. Better standards frees up contract developers to focus on solving business problems instead of correctness problems.

Are you suggesting there is another route for CAP-0049 we could explore outside of ERC-20?

> How does things look like for tokens that are issued on the smart side, how do they get exposed to classic? I believe that "classic" (or a better version of it), is the future for some highly specialized operations on the network (like payments) so supporting this direction of interop is critical.

I think a huge draw for developers building Stellar smart contract with tokens (LP shares, etc) is the payment rails that Stellar classic provides. As such, having an easy way to move CAP-0049/0048 compliant (be it ERC-20 or something similar) smart contract tokens onto Stellar classic is a differentiator no other chain can provide. Further, making this process smooth will help curb the likely issue of liquidity fragmentation we will see with funds being deployed to smart contract uniswap clones, classic Liquidity Pools, and the DEX.

> One thing I didn't cover as I do not completely understand it here is Alex's comment on centralized vs decentralized model. Is that in relation to the governance model for tokens? Some allow "free flow" like tokens that are not "auth required" vs ones that require the issuer to be involved in some way.

My comment was in regards to the way we hope users access the Stellar blockchain and subsequent protocols in the future. This was confusing word choice on my part, better words would be centralized = custodial services, and decentralized = self-custody. CAP-0049 has some known quirks for usage that a custodial service will be able to best smooth out, at least in the short term. For example, if I hold classic USDC and want to deposit into a lending protocol, under self-custody I will have to sign a transaction to wrap my USDC, then sign a transaction to deposit into the protocol. A custodial service could manage this in one click.

Best,
Alex

Reply all
Reply to author
Forward
0 new messages