Skip to main content

Autocallable note

Introduction

This contract is the adaptation to the Tezos blockchain of an autocallable note issued by Goldman Sachs.

AutoCallable notes are short-term market-linked investments offering an above-market coupon if automatically matured prior to the scheduled maturity date. The product is automatically matured (“auto-called”) if the reference asset is at or above its initial level on a predetermined observation date. If called, the investor will receive their initial principal investment plus an above-market coupon.

Because of the passive nature of the blockchain, the contract here is an escrow of the issuer's payments (interests and redeem amounts in due time). At any time, the holder can check whether the escrowed amount is equal to what was initially planned by the contract, by calling the check entrypoint. If the balance is not equal to the expected amount, the contract goes to state Defaulted. The holder may then turn to a dispute resolution mecanism to recover the due amount, the contract state being the proof of the default in payment.

Fixing values are provided to the contract by the oracle address. Knowing that these data are licensed by market places to their customers, it is an open question to know whether the publically available blockchain data is a no-go. The challenge for the DeFi is to find business models not based on fixing values, but rather on the operation of such contracts.

Thank you to Alain Frisch, CTO at Lexifi, for this example contract and his help to adapt it. Lexifi provides financial services with a DSL (Domain Specific Language) to execute and simulate financial contracts. Alain Frish also provided the corresponding Lexifi code, automatically extracted by Lexifi drivers from the above document.

API

The contract relies on the 3 underlyings: Bank of America, Société Générale and Union des Banques Suisses.

Storage

NameTypeDescription
issueraddressIssuer address, transfers note payments.
holderaddressHolder address, checks the contract balance.
oracleaddressOracle address, provides fixing values.
nominaltezNominal value transfered by holder to confirm start of contract.
tradedateTrade date.
finaldateFinal Fixing date.
gredemptiondateRedemption date.
bac_initialrationalInitial Bank of America Fixing rate.
sg_initialrationalInitial Société Générale rate.
ubs_initialrationalInitial Union des Banques Suisses rate.
bac_strikerationalBank of America strike rate.
sg_strikerationalSociété Générale strike rate.
ubs_strikerationalUnion des Banques Suisses strike rate.
earlycollectionEarly redemption, defined by:
  • Observation date
  • Early redemption date
  • Trigger percentage
  • Early redemption value

If on one of the Observation dates, the Fixing of each Underlying is equal to or above its respective Trigger Percentage multiplied by the Fixing (Initial), the Note will be redeemed and the Investor will receive on the respective Early Redemption Date an amount equal to the relevant Early Redemption Value multiplied by the Nominal.
interestcollectionInterest, defined by:
  • Interest Observation Date
  • Interest Payment Date
  • Interest Barrier Percent
  • Interest Rate

If on one of the Interest Observation Dates, the Fixing of each Underlying is equal to or above its respective Interest Barrier Percent multiplied by the Fixing (Initial) , the Investor will receive on the respective Interest Payment Date an amount equal to the relevant Interest Rate multiplied by the Nominal.
fixingcollectionFixing value defined by:
  • Observation Date
  • Bank of America Fixing
  • Société Générale Fixing
  • Union des banques Suisses

Fixing values are provided by oracle.
actual_paymenttezTotal payment by issuer.
state_statesOne of:
  • Created (intial state)
  • Canceled (holder or issuer has canceled the contract)
  • Defaulted
  • Terminated

Entrypoints

NameParametersDescription
add_fixingffobservation, fbac, fsg, fubsCalled by oracle to provide Fixing values for underlyings.
pay_noteCalled by issuer to transfer payment notes.
confirmCalled by holder who transfers nominal amount to issuer via contract. Sets contract state to Confirmed.
cancelCalled by holder or issuer to cancel contract.
checkCalled by holder to compare actual payments with expected payments. If different, sets the contract to Defaulted state.
terminateCalled by holder to terminate contract.

Code

autocallable.arl
archetype autocallable(
issuer : address,
holder : address,
oracle : address,
nominal : tez,
trade : date,
final : date,
gredemption : date,
(* UNDERLYINGS *)
bac_initial : rational,
sg_initial : rational,
ubs_initial : rational,
bac_strike : rational,
sg_strike : rational,
ubs_strike : rational
)

asset early {
eobservation : date;
redemption : date;
trigger : rational;
value : rational;
}

asset interest {
iobservation : date;
payment : date;
barrier : rational;
rate : rational;
}

asset fixing {
fobservation : date;
bac : rational; (* Bank of America Corporation *)
sg : rational; (* Societe Generale *)
ubs : rational; (* Union des Banques Suisses *)
}

(* EXPECTED PAYMENT COMPUTATION *)
function compute_expected (d : date) : tez {
var expected = 0tz;
var terminated = false;
var redeem_date = final;
(* early redemption *)
for e in early do
if early[e].redemption <= d then begin
(* is there early redemption ? *)
var ee = early[e].eobservation;
if fixing[ee].bac >= early[e].trigger * bac_initial
and fixing[ee].sg >= early[e].trigger * sg_initial
and fixing[ee].ubs >= early[e].trigger * ubs_initial
then begin
expected += early[e].value * nominal;
redeem_date := early[e].eobservation;
terminated := true
end
end
done;
(* redemption *)
if not terminated and gredemption <= d then
if fixing[gredemption].bac >= bac_strike
and fixing[gredemption].sg >= sg_strike
and fixing[gredemption].ubs >= ubs_strike
then
expected += nominal
else begin
var bac_trigger = fixing[gredemption].bac / bac_strike;
var sg_trigger = fixing[gredemption].sg / sg_strike;
var ubs_trigger = fixing[gredemption].ubs / ubs_strike;
var worst = min ((min (bac_trigger, sg_trigger)), ubs_trigger);
expected += worst * nominal
end;
(* expected interests *)
var exp_interests = 0tz;
for i in interest do
if interest[i].iobservation <= redeem_date and interest[i].payment <= d
then begin
var ii = interest[i].iobservation;
if fixing[ii].bac >= interest[i].barrier * bac_initial
and fixing[ii].sg >= interest[i].barrier * sg_initial
and fixing[ii].ubs >= interest[i].barrier * ubs_initial
then exp_interests := interest[i].rate * nominal
end
done;
expected += exp_interests;
return expected
}

(* PAYMENT action *)
variable actual_payment : tez = 0tz

entry pay_note () {
called by issuer
effect {
actual_payment += transferred
}
}

entry add_fixing (
ffobservation : date,
fbac : rational,
fsg : rational,
fubs : rational) {
fixing.add({ffobservation; fbac; fsg; fubs})
}

(* STATE MACHINE *)
states =
| Created initial (* doc initial state. *)
| Canceled (* holder or issuer has canceled the transaction. *)
| Confirmed (* holder has confirmed. *)
| Defaulted
| Terminated

(* Used by holder to confirm transaction.
It transfers the price of contract (nominal) *)
transition confirm () {
called by holder
from Created
to Confirmed when { transferred = nominal }
with effect { transfer transferred to issuer }
}

transition cancel () {
called by holder or issuer
from Created
to Canceled
}

transition check () {
called by holder
from Confirmed
to Defaulted when { actual_payment < compute_expected(now) }
}

transition terminate () {
called by issuer
from Confirmed
to Terminated when { actual_payment >= compute_expected(now) }
}