Skip to content

Latest commit

 

History

History
317 lines (250 loc) · 10.1 KB

cap-0048.md

File metadata and controls

317 lines (250 loc) · 10.1 KB

Preamble

CAP: 0048
Title: Smart Contract Asset Interoperability
Working Group:
    Owner: Jonathan Jove <@jonjove>
    Authors:
    Consulted: Nicolas Barry <@monsieurnicolas>, Leigh McCulloch <@leighmcculloch>, Tomer Weller <@tomerweller>
Status: Rejected
Created: 2022-05-02
Discussion: TBD
Protocol version: TBD

Simple Summary

Allow smart contracts to interoperate with Stellar assets.

Motivation

There is an existing ecosystem of assets on the Stellar Network. Smart contracts on the Stellar Network will be significantly less useful if it is not possible to interact with those assets. Therefore, we must build an interoperability layer.

Requirements

Performance

Tokens are the basic unit of blockchain applications, and as such they should be very efficient to use.

Trust Transitivity

It is not guaranteed that every anchor will take steps to maximize interoperability between their existing asset and smart contracts. Other organizations may try to fill that gap, but they will not necessarily have the same level of trust. For example, consider an asset X issued by account A. If A deploys smart contracts to maximize interoperability between their existing asset and other smart contracts, then these smart contracts don't require trusting an additional organization. Native support to interoperate between an existing asset and smart contracts would avoid this issue by using trust in the Stellar protocol.

Restricted Privileges

ERC-20 tokens use an allowance model to limit how much of a user's balance can be spent by a specific smart contract. This makes it relatively safe to interact with a smart contract, because it cannot steal arbitrarily large amounts of money. Existing Stellar assets have no such functionality. As a consequence, it is extremely dangerous to let a smart contract execute a payment with any account (other than the contract account) as source.

Goals Alignment

This CAP is aligned with the following Stellar Network Goals:

  • The Stellar Network should make it easy for developers of Stellar projects to create highly usable products
  • The Stellar Network should enable cross-border payments, i.e. payments via exchange of assets, throughout the globe, enabling users to make payments between assets in a manner that is fast, cheap, and highly usable.

Abstract

This proposal provides an ERC-20 compliant interface (excluding totalSupply()) for Stellar assets. This is achieved by introducing a new type of ledger entry, AllowanceEntry, which is used to record ERC-20 style allowances. Atop this, a ContractID for cross-contract invocations is introduced which can refer to an actual smart contract or to native contracts. In this case, we provide a new type of native contract with type ASSET_ADAPTOR which implements the ERC-20 interface in terms of accounts, trustlines, and allowances.

Specification

XDR Changes

diff --git a/src/xdr/Stellar-ledger-entries.x b/src/xdr/Stellar-ledger-entries.x
index 377309f9..0337128b 100644
--- a/src/xdr/Stellar-ledger-entries.x
+++ b/src/xdr/Stellar-ledger-entries.x
@@ -100,7 +100,8 @@ enum LedgerEntryType
     CLAIMABLE_BALANCE = 4,
     LIQUIDITY_POOL = 5,
     CONTRACT_CODE = 6,
-    CONTRACT_DATA = 7
+    CONTRACT_DATA = 7,
+    ALLOWANCE = 8
 };
 
 struct Signer
@@ -499,6 +500,21 @@ struct ContractDataEntry {
     SCVal *val;
 };
 
+struct AllowanceEntry
+{
+    union switch (int v)
+    {
+    case 0:
+        void;
+    }
+    ext;
+
+    AccountID transferFrom;
+    AccountID spender;
+    Asset asset;
+    int64 amount;
+};
+
 struct LedgerEntryExtensionV1
 {
     SponsorshipDescriptor sponsoringID;
@@ -533,6 +549,8 @@ struct LedgerEntry
         ContractCodeEntry contractCode;
     case CONTRACT_DATA:
         ContractDataEntry contractData;
+    case ALLOWANCE_ENTRY:
+        AllowanceEntry allowance;
     }
     data;
 
diff --git a/src/xdr/Stellar-transaction.x b/src/xdr/Stellar-transaction.x
index 3d9ee3ea..fa6297a7 100644
--- a/src/xdr/Stellar-transaction.x
+++ b/src/xdr/Stellar-transaction.x
@@ -14,6 +14,20 @@ case LIQUIDITY_POOL_CONSTANT_PRODUCT:
     LiquidityPoolConstantProductParameters constantProduct;
 };
 
+enum ContractType
+{
+    SMART_CONTRACT = 0,
+    ASSET_ADAPTOR = 1
+};
+
+union ContractID switch (ContractType type)
+{
+case SMART_CONTRACT:
+    int64 contractID;
+case ASSET_ADAPTOR:
+    Asset asset;
+};
+
 struct DecoratedSignature
 {
     SignatureHint hint;  // last 4 bytes of the public key, used as a hint

Semantics

Asset Adaptors

Asset adaptors implement the ERC-20 interface in terms of accounts, trustlines, and allowances. They allow smart contracts that can interoperate with ERC-20 tokens to interoperate with Stellar assets.

Specifically, asset adaptors implement the following

// name is intended to comply with SEP-11
// Returns "NATIVE" for ASSET_TYPE_NATIVE
// Returns format!("{}:{}", assetCode, issuer) for ASSET_TYPE_CREDIT_ALPHANUM4
// Returns format!("{}:{}", assetCode, issuer) for ASSET_TYPE_CREDIT_ALPHANUM12
fn name() -> String;

// symbol is intended to comply with SEP-11
// Returns "NATIVE" for ASSET_TYPE_NATIVE
// Returns assetCode for ASSET_TYPE_CREDIT_ALPHANUM4
// Returns assetCode for ASSET_TYPE_CREDIT_ALPHANUM12
fn symbol() -> String;

// Returns 7
fn decimals() -> u8;

// Returns Account(owner).balance for ASSET_TYPE_NATIVE
// Returns TrustLine(owner, asset).balance for ASSET_TYPE_CREDIT_ALPHANUM4
// Returns TrustLine(owner, asset).balance for ASSET_TYPE_CREDIT_ALPHANUM12
fn balanceOf(owner: AccountID) -> uint256;

// Executes PaymentOp {
//     sourceAccount: msg.sender,
//     destination: to,
//     asset: asset,
//     amount: value,
// }
// Returns PaymentResultCode == PAYMENT_SUCCESS
fn transfer(to: AccountID, value: uint256) -> bool;

// Loads a = allowance(from, msg.sender) 
// Returns false if a < amount
// Sets ApprovalEntry {
//     transferFrom: msg.sender,
//     spender: spender,
//     asset: asset,
//     amount: a - value,
// }
// Executes PaymentOp {
//    sourceAccount: msg.sender,
//    destination: to,
//    asset: asset,
//    amount: value,
// }
// Returns PaymentResultCode == PAYMENT_SUCCESS
fn transferFrom(from: AccountID, to: AccountID, value: uint256) -> bool;

// Sets ApprovalEntry {
//     transferFrom: msg.sender,
//     spender: spender,
//     asset: asset,
//     amount: value,
// }
// Returns true on success
fn approve(spender: AccountID, value: uint256) -> bool;

// Returns Allowance(owner, spender, asset).amount
fn allowance(owner: AccountID, spender: AccountID) -> uint256;

Note that totalSupply() is not implemented.

Asset Adaptor Contract Address

Asset adaptors don't have normal contract addresses, because they always exist and have no on-chain representation. Instead, an asset adaptor contract address is simply identified by a ContractID of type ASSET_ADAPTOR that contains the specified asset. We will need a host function like

fn get_asset_adaptor(asset: Asset) -> ContractID;

Design Rationale

Asset Adaptors Don't Implement totalSupply()

In this proposal, asset adaptors don't implement totalSupply() because the Stellar protocol does not track issuance of non-native assets. While being able to determine the total supply of an arbitrary asset is useful for front-ends, it doesn't seem to be essential for smart contracts.

If we determine that this function is necessary, we could implement issuance tracking although this would require a fairly significant amount of effort.

Trustlines

In this proposal, asset adaptors require a trustline to exist in order to complete a payment. This is done to preserve the existing behavior around trustline creation. Furthermore, it doesn't add any new edge cases because in general a trustline might require authorization.

This is a bit inconvenient compared to the usual ERC-20 implementation where you can simply send payments to any address, but should be acceptable nonetheless.

Issue: Issuer Balance

The issuer of a non-native asset doesn't have a balance of the asset in the traditional sense. When the issuer sends a payment, it mints the asset. When the issuer receives a payment, it burns the asset.

What should balance(issuer) return? Should transfer() mint when msg.sender is the issuer?

Issue: Not Compatible With High Supply Assets

A Stellar asset with total supply greater than INT64_MAX can break contracts that treat Stellar assets like any other ERC-20. For example, consider a liquidity pool contract. It holds reserves of two assets. If both assets were high supply Stellar assets and someone deposited enough of each asset that the reserves were INT64_MAX of each, then the contract would be frozen with only withdrawals possible. This occurs because the contract trustlines can't hold more than INT64_MAX.

One potential option to alleviate this issue would be to support trustlines with higher limits.

Issue: Classic Payments To Contracts

This is a relatively minor user experience issue. An account can use a Stellar payment to send assets to a contract, but this is almost certain not to have the desired effect. The reason is that smart contracts do not execute while applying Stellar operations. Therefore, the contract will not be aware that it received assets or from whom.

Should we take any steps to prevent this?

Issue: Available Balance vs Balance

The Stellar protocol allows part of an account or trustline to be encumbered as a liability. This means that it is generally not true that a contract can check an accounts balance to determine whether it can make a payment. Should the balanceOf(owner) function return the owner's balance or the owner's available balance?

Protocol Upgrade Transition

Backwards Incompatibilities

This proposal is completely backwards compatible. It describes a new interface to existing behavior that is accessible from smart contracts, but it does not modify the existing behavior.

Resource Utilization

This proposal adds a new type of ledger entry, which will likely lead to an increase in the total size of the ledger.

Test Cases

None yet.

Implementation

None yet.