Translating Daml concepts to Solidity

I’d like to understand how the Daml language concepts compare to other smart-contract languages like Solidity. How could one represent or encode Daml contracts, parties, signatories, observers, controllers, etc. in Solidity? To what extent is such a representation possible and where would it break down?

For example, the following SimpleIou in Daml:

data Cash = Cash with
  currency : Text
  amount : Decimal
    deriving (Eq, Show)

template SimpleIou with
    issuer : Party
    owner : Party
    cash : Cash
  where
    ensure cash.amount > 0.0
    observer owner
    signatory issuer

    choice SimpleTransfer : ContractId SimpleIou with
        newOwner : Party
      controller owner
      do create this with owner = newOwner

In my first attempt, I’d encode template, signatory, and ensures clause roughly as follows in Solidity. The idea is that the message sender msg.sender carries the authorization.

contract SimpleIou {

  address issuer;
  address owner;
  Cash cash;

  constructor(Cash aCash, address aOwner) payable {
    require(aCash.amount > 0.0);

    issuer = msg.sender; // Convention: The message sender is the controller
    owner = aOwner;
    cash = aCash;
  }

There’s no counterpart for observers, but if I assume that all interested parties can read all blocks, then they can themselves figure out when they are an observer to a SimpleIou contract.

If I go for mutable Solidity SimpleIous, the SimpleTransfer choice looks fairly simple:

  modifier onlyOwner {
    require(
      msg.sender == owner,
      "Only owner can call this function."
    );
    _
  }

  function simpleTransfer(address newOwner) public onlyOwner {
    owner = newOwner;
  }

However, if I want to retain the UTXO model and really create a new contract upon a transfer, it gets tricky: The new Iou must be created by the issuer whereas the simpleTransfer choice must be called by the owner. So the simpleTransfer choice in SimpleIou would have to call an external function in the issuer’s contract.

So maybe msg.sender is not an adequate approach to deal with delegation. Are there better ways to deal with this in Solidity?

3 Likes

Hi,

I always think that the concepts of contract a bit different in Daml and in Solidity. In general contracts in Daml, whether it is active or archived, are “states” inside a deployed contract in Ethereum (written in Solidity).

With this, following this example, I will first code Solidity with Struct, which is close to the fields in a template, such as

struct Iou { 
   address issuer;
   address owner;
   uint256 amount;
   bool archived;
}

Then a variable using mapping to point an index to the Iou struct. Note that I put a bool to keep whether it is archived.

For example, the contract in Ethereum will keep a “database” of things like this
#1 => issuer=bank, owner=alice, amount=100, archive=false
#2 => issuer=bank, owner=bob, amount = 500, archive=true
#3 => issuer=bank, owner=charlie, amount = 500, archive=false

With that, we can create some functions (loosely close to choice in Daml),. One possible is transfer(), which can only be called by owner, and the result is to create a new entry (with a new index where archived = false) while the previous one is archived (from false to true).

This implementation roughly achieves the UTXO model.

More functions can be added. For example, query() on a particular addresses, etc. This involves some logic to make it more user-friendly.

It is how I would compare a Daml code vs. a Solidity code.

cheers,
kc

3 Likes

IIUC both your and @kctam’s approaches are trying to mimic Daml’s UTXO model in Ethereum. I haven’t written Solidity in a long time so this may be outdated, but the typical way to represent an IOU in Ethereum would be as an ERC-20 contract, i.e. a single contract, issued by issuer which holds a map owner -> amount. So instead of a UTXO per holding you have a single contract tracking all holdings of the issuer’s cash IOUs.

Under this scenario, I’d roughly map:
signatories → Ethereum miners
observers → Ethereum full node operators
submitter → msg.sender
controllers → Any msg.sender where the require doesn’t fail

2 Likes

Indeed, it is customary on the EVM that a contract manages many instances that would be separate Daml contract instances. This however doesn’t make that much of a difference when mapping Daml parties in their different roles. One could group Daml contract instances by the group of signatories into EVM contracts so that an EVM contract manages all instances for a given group of signatories. But then we’d somehow have to tie the group of signatories with the address of this contract in a way that’s verifiable in other contracts (e.g., via the msg.sender pattern for authorization).

Under this scenario, I’d roughly map:
signatories → Ethereum miners
observers → Ethereum full node operators
submitter → msg.sender
controllers → Any msg.sender where the require doesn’t fail

I don’t think it makes sense to unify Daml parties with entities that operate on the Ethereum network themselves. A Daml party typically need not run a node; rather a signatory trusts the entity operating the node where they are hosted, and the ledger operator that the participant node uses.

I see the analogy with msg.sender for the controller. The submitter is probably tx.origin instead of msg.sender, isn’t it?

1 Like

I agree, maybe in a simplified 1:1 Party-Participant mapping scenario, these concepts would map better.

Yes, you’re right