How Eternl's Staking Vault works

The Eternl wallet app has lately introduced their Staking Vault in an attempt to secure the financing of their wallet app.

The idea is that you lock an amount of ADA (between 500 and 1 million ADA) for a period of your choice (between 12 and 36 epochs). Since it is locked, they can pledge the whole amount in a private pool, get higher rewards, and give you a share of it, so that you also get higher rewards than in a “normal” pool.

I am more or less neutral on the question if this is a good thing. It has the potential to take away stake from our beloved single pool operators, but then again it gives some reliable financing to a quite good wallet app. The concept might not survive implementation of CIP - Shelley’s Basho-Voltaire decentralization update or similar proposals that take away the quite huge boost of pledging.

What I am interested in is how it works technically. It claims to be non-custodial, the ADA are supposed to stay under my control, although they are locked for some time. How does that work?

So, when I locked 500 ADA (together with a Mesmerizer NFT that gives a small additional boost) this transaction was built:
https://cardanoscan.io/transaction/0c5e58a0bd178247339cf2c6bdfd86f59fe6f73afb38e46421d02f46451d6df3

It has moved the 500 ADA and the NFT to this address:
https://cardanoscan.io/address/addr1z893707c0cmrdc2v4zflh45uzvkeyl3k8xfz63snzhzgwyqfzup4e9umthe6xgvhcxu4d5cygcj0h3vdslrt3whadmns2d9mdf
And this address is delegated to this pool:
https://cardanoscan.io/pool/pool16u5trhs7ysdh2e8aadk0p8xp52sczl8s29ay6ythv3sxujmdfeu
As expected that pool has a margin of 100%, so all rewards go to the pool operators (and I have to trust their word that they will give me the announced share – which I do). And all stake is pledged (since there are only three delegating stake addresses/keys, which are all pool owners at the same time). The committed pledge is a little below the active pledge, because it seems to be adjusted to newly vaulted funds only once per epoch (5.93 million committed, 6.54 million active at the time of writing).

The metadata of the locking transaction give all the details of the vault locking – agreed rewards, pool that is delegated to, amount, and also the exact unlocking slot and the hash of the key that I can use to move the funds back after unlocking.

The Staking Vault uses a simple native script to achieve this locking. You may have seen native scripts, when dealing with NFTs or other native tokens. They can express conditions like: “A transaction may only be done after this slot, before that slot, and has to be signed by this key and one of those keys.”

With the information from the metadata, we can construct the locking script:

{
  "type": "all",
  "scripts":
  [
    {
      "type": "sig",
      "keyHash": "f2e912d4a884392b41994383999168962d2fdd3b727586e39cc22c74"
    },
    {
      "type": "after",
      "slot": 73180801
    }
  ]
}

This means that funds on this contract address can be moved by the key with the given hash, but only after the slot with the given number has passed.
We can verify that this is really the script for this contract address on Cardanoscan:
https://cardanoscan.io/address/addr1z893707c0cmrdc2v4zflh45uzvkeyl3k8xfz63snzhzgwyqfzup4e9umthe6xgvhcxu4d5cygcj0h3vdslrt3whadmns2d9mdf?tab=script
(The script is only available on Cardanoscan after it has been given manually, because it is not stored on the blockchain itself, but only its hash is contained in the address.)

The unlocking slot at the beginning of epoch 367 is computed by: (367−208)×432 000+4 492 800+1=73 180 801 (Epoch 208, slot 4 492 800 was the beginning of the Shelley era and after that each epoch has exactly 432 000 slots/seconds.)

Do I have the key for moving the ADA back once that time has come even without Eternl’s help?

Through some talks with the Eternl people, I know they use the key with the derivation path m/1854'/1815'/0'/0/0 for the staking vault. (If you know a bit about derivation paths, you might notice that the first component is usually 1852' in Cardano. This is because the purpose 1854 is reserved for multi-signature scripts, which the thing we are doing here is an instance of – although we only deal with one signature here.)

Since I use a Ledger, I can get the public key hash by:

$ cardano-hw-cli address key-gen --path 1854H/1815H/0H/0/0 --verification-key-file hw-vault.vkey --hw-signing-file hw-vault.hwsfile
$ cardano-cli address key-hash --payment-verification-key-file hw-vault.vkey
f2e912d4a884392b41994383999168962d2fdd3b727586e39cc22c74

And that is exactly the key hash shown in the locking transaction and used in the locking script of the contract address.

If it would have been a seed phrase wallet, we could have used:

$ echo abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address \
> | cardano-address key from-recovery-phrase Shelley \
> | cardano-address key child 1854H/1815H/0H/0/0 \
> | cardano-address key public --without-chain-code \
> | cardano-address key hash --hex
21b764befa036404b9a2651de3f544379d01ae841bba88d87a837188

or

$ echo abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon address \
> | cardano-address key from-recovery-phrase Shelley \
> | cardano-address key child 1854H/1815H/0H/0/0 > sw-vault.xsk
$ cardano-cli key convert-cardano-address-key --shelley-payment-key --signing-key-file sw-vault.xsk --out-file sw-vault.skey
$ cardano-cli key verification-key --signing-key-file sw-vault.skey --verification-key-file sw-vault.vkey
$ cardano-cli address key-hash --payment-verification-key-file sw-vault.vkey
21b764befa036404b9a2651de3f544379d01ae841bba88d87a837188

Now, I know that I know the contract script as well as that I have access to the private key for the key hash given in that script. After slot 73 180 801 has passed, I could build a transaction moving the funds back to my wallet even without the help of Eternl (using the JSON above as --tx-in-script-file and signing the transaction with cardano-hw-cli with the 1854H/1815H/0H/0/0 key afterwards). So, this is really a non-custodial solution, where I keep the full control over the locked assets (but have to trust the Eternl team about really getting the promised rewards).

14 Likes

I love such threads, they are so valuable for the community!

Thanks alot @HeptaSean

2 Likes

Awesome, I’ve been wondering how that worked. Thanks for the explanation :+1:

1 Like

Thanks. This is a nice explanation
One thing this does not cover is catalyst voting. The vault staking address will get the snapshot of the Ada power for Catalyst voting
So Eternal will get the voting power and its like you have delegated your vote to Eternl

2 Likes

Yes, they could, but I would suppose they don’t use it. We could search the chain for a Catalyst voting registration.

By the way: We have a quite similar situation with exchanges, with the huge funds of IOG, Emurgo, and the Cardano Foundation, and with the ADA locked in the emerging DEXes. I believe, none of them use their voting power, but they could.

1 Like

I was looking for this explanation! Really appreciate you wrote this for community, sir!! :saluting_face:

I have one request for you: I want to translate this topic for Indonesian Forum, is it okay for you?

Of course! Feel free.

1 Like

Alrighty! Thanks for your confirmation, sir!

Ta daa!

1 Like

I find it a bit strange that Cardanoscan calls a native script address a ‘contract address’. Simple native scripts shouldn’t be called smart contracts imho. Because if you do, you also have to say that Bitcoin has smart contracts, because such scripts also exist there. And clearly most of us would agree that Bitcoin doesn’t have smart contracts.

More info about native scripts can be found on cardano-node/simple-scripts.md at master · input-output-hk/cardano-node · GitHub btw.

…, but it is a contract address. It does not have a private key associated with it that can arbitrarily spend the funds on that address, but rather a script – a native script in this case – and the payment part of the address is the hash of that script instead of the hash of a public key.

Conceptually, it is much closer to a smart contract than to a “normal” address.

Perhaps, they could write “'script address” instead of “contract address”. That is exactly what it is according to https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019#shelley-addresses. It starts with addr1z like all delegated script addresses – Plutus or native – and not with addr1q or one of the other possibilities.

BTW: Cardanoscan cannot even easily decide if the script associated with an address is native or Plutus. It cannot be deduced from the address alone, but only be seen in transactions successfully spending UTxOs of that address.

Then they should use the term script address, which is the correct terminology imho. ‘Behind’ such script address can either reside a native script or a smart contract then (which can only be known after the native script of smart contract script is provided like you said). Maybe I’m nitpicking…

1 Like

Update: Staking Vault will not accept new locks:

Too bad, I really liked the idea and the technological realisation of it.

2 Likes

As a final update: My Staking Vault got unlocked now. So, I thought I’ll transfer it back into my wallet by the method shown in this post – without any help of the Eternl user interface … although that would have, of course, also been possible.

Since I use a Ledger, this is using cardano-hw-cli to generate the key files and sign the transaction.

Preparation:

$ cardano-hw-cli address key-gen \
> --path 1854H/1815H/0H/0/0 \
> --verification-key-file hw-vault.vkey \
> --hw-signing-file hw-vault.hwsfile
$ cat vault.script
{
  "type": "all",
  "scripts":
  [
    {
      "type": "sig",
      "keyHash": "f2e912d4a884392b41994383999168962d2fdd3b727586e39cc22c74"
    },
    {
      "type": "after",
      "slot": 73180801
    }
  ]
}

Get the UTxOs on the vault address:

$ cardano-cli query utxo --mainnet \
> --address addr1z893707c0cmrdc2v4zflh45uzvkeyl3k8xfz63snzhzgwyqfzup4e9umthe6xgvhcxu4d5cygcj0h3vdslrt3whadmns2d9mdf
                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
0c5e58a0bd178247339cf2c6bdfd86f59fe6f73afb38e46421d02f46451d6df3     0        500000000 lovelace + 1 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb.6d65736d6572697a65723034303538 + TxOutDatumNone
aaf0de8dfc243edb471ac8b7ad38ef8d34c34b425c412ba65446c1523abc6d04   140        11350005 lovelace + TxOutDatumNone

The first UTxO are my locked ADA together with the Mesmerizer put in there for an additional boost. The second UTxO are the rewards paid by Eternl as promised.

Build the transaction:

$ cardano-cli transaction build --mainnet \
> --tx-in 0c5e58a0bd178247339cf2c6bdfd86f59fe6f73afb38e46421d02f46451d6df3#0 \
> --tx-in aaf0de8dfc243edb471ac8b7ad38ef8d34c34b425c412ba65446c1523abc6d04#140 \
> --tx-out "addr1qyh72hvvrurjvddx4gq37jd2fzyef8scz9cwcyc90dffq0xxllh3nc5r82ujj36fy9zh0gryqvqy7r3ejd2h2kgsvryswhjr9q+2824075+1 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb.6d65736d6572697a65723034303538" \
> --change-address addr1qyh72hvvrurjvddx4gq37jd2fzyef8scz9cwcyc90dffq0xxllh3nc5r82ujj36fy9zh0gryqvqy7r3ejd2h2kgsvryswhjr9q \
> --tx-in-script-file vault.script --invalid-before 73180801 \
> --cddl-format --out-file transaction.raw
Estimated transaction fee: Lovelace 174037

The two --tx-ins take the UTxOs found above. The --tx-out puts the Mesmerizer on one output on the address of the target account with a couple of ADA (in fact, the amount accompanying all my tokens in this account … OCD …). The --change-address puts all the rest on the same address without having to calculate anything. The --tx-in-script-file is the one reconstructed in the original post, which also demands the --invalid-before to be present. The new --cddl-format for the --out-file makes it easier (or is already needed?) to use cardano-hw-cli.

Witness with the Ledger, assemble signed transaction and submit:

$ cardano-hw-cli transaction witness --mainnet \
> --tx-file transaction.raw \
> --hw-signing-file hw-vault.hwsfile \
> --out-file transaction.witness
$ cardano-cli transaction assemble \
> --tx-body-file transaction.raw \
> --witness-file transaction.witness \
> --out-file transaction.signed
$ cardano-cli transaction submit --mainnet \
> --tx-file transaction.signed
Transaction successfully submitted.

And the funds are back in my account:
screenshot-2022-10-03-12:25:19
Which is also reported in Eternl’s Staking Vault tab:
screenshot-2022-10-03-12:27:18
Where I could have done this without playing around on the command line.

It was a really nice idea! Thanks so much Eternl team! Too bad, it didn’t fly!

1 Like

Technically they never left I think?

1 Like

Ooh, terminology gets a little arbitrary here, I fear.

The stake part of the address definitely was a key hash outside of my wallet – one of the owners of Eternl’s staking vault pool.

They payment part was a script hash of a script allowing a key from my wallet to retrieve, but only with an additional time-lock constraint. That is kind of … half in my wallet?

1 Like

In your wallet but time locked :wink:
I see the rewards also resided on your payment address (sent there manually by Eternl?), so that’s also in your wallet.
Only rewards not sent to your payment address by Eternl yet weren’t in your wallet imho.

Yes. Manually or scripted.

That part was not ensured by the chain, because the concept was that they get rewards for a high pledge on their owner addresses and share it with us. So, it had to be distributed by them.

1 Like