Cash Tokens based on Zeto¶
The code for this tutorial can be found in example/zeto.
This shows how to leverage the Zeto in order to build a cash payment solution, for instance wholesale CBDC or a payment rail with commercial bank money, with privacy, illustrating multiple aspects of Paladin's privacy capabilities.
Running the example¶
Follow the Getting Started instructions to set up a Paldin environment, and then follow the example README to run the code.
Scenario #1: cash solution with private minting¶
In this scenario, the Zeto tokens are directly minted by the authority in the Zeto contract, making the mint amounts private. This also means the total supply of the Zeto tokens is unknown to the participants. Only the authority performing the minting operations is aware of the total supply.
Below is a walkthrough of each step in the example, with an explanation of what it does.
Create CBDC token¶
const zetoFactory = new ZetoFactory(paladin3, 'zeto');
const zetoCBDC = await zetoFactory.newZeto(cbdcIssuer, {
tokenName: 'Zeto_AnonNullifier',
});
This creates a new instance of the Zeto domain, using the Zeto_AnonNullifier contract. This results in a new cloned contract on the base ledger, with a new unique address. This Zeto token contract will be used to represent tokenized cash/CBDC.
The token will be minted by the central bank/CBDC issuer party. Minting is restricted to be requested only by the central bank, the deployer account of the contract.
Issue cash¶
let receipt = await zetoCBDC.mint(cbdcIssuer, {
mints: [
{
to: bank1,
amount: 100000,
},
{
to: bank2,
amount: 100000,
},
],
});
The cash issuer mints cash to the commercial banks, bank1
and bank2
.
Bank1 transfers tokens to bank2 as payment¶
receipt = await zetoCBDC.using(paladin1).transfer(bank1, {
transfers: [
{
to: bank2,
amount: 1000,
},
],
});
Bank1 can call the transfer
function to transfer zeto tokens to multiple parties, up to 10. Note that the identity bank1
exists on the paladin1
instance,
therefore it must use that instance to send the transfer transction (.using(paladin1)
).
Scenario #2: cash solution with public minting¶
This scenario supports the requirement to make the total supply of the cash tokens public. This is achieved by making the authority perform the minting operations in an ERC20 contract. The participants can then exchange their ERC20 balances for Zeto tokens, by calling deposit
, and exchange back to their ERC20 balances by calling withdraw
.
Below is a walkthrough of each step in the example, with an explanation of what it does.
Create CBDC token¶
const zetoFactory = new ZetoFactory(paladin3, 'zeto');
const zetoCBDC = await zetoFactory.newZeto(cbdcIssuer, {
tokenName: 'Zeto_AnonNullifier',
});
This creates a new instance of the Zeto domain, using the Zeto_AnonNullifier contract. This results in a new cloned contract on the base ledger, with a new unique address. This Zeto token contract will be used to represent tokenized cash/CBDC.
Create public supply token (ERC20)¶
This deploys the ERC20 token which will be used by the authority to regulate the CBDC supply, with transparency to the paricipants.
Configure the Zeto token contract to accept deposits and withdraws from the ERC20¶
When the deposit
function is called on the Zeto contract, this ERC20 contract will be called to draw the requested funds from the depositor's account. Conversely, when the withdraw
function is called, this ERC20 contract will be called to transfer back the ERC20 balance to the withdrawer's account.
Mint ERC20 tokens to publicly regulate CBDC supplies¶
Because the ERC20 implementation provides full transparency of the token operations, minting in the ERC20 allows all blockchain network participants to be aware of the overall supply of the CBDC tokens.
Banks exchange ERC20 balances for Zeto tokens - deposit¶
After having been minted ERC20 balances, a partcipant like bank1
can call deposit
on the Paladin Zeto domain to exchange for Zeto tokens. Behind the scenes, the ERC20 balance is transferred to the Zeto contract which will hold until withdraw
is called later.
Bank1 transfers tokens to bank2 as payment¶
receipt = await zetoCBDC.using(paladin1).transfer(bank1, {
transfers: [
{
to: bank2,
amount: 1000,
},
],
});
Bank1 can call the transfer
function to transfer zeto tokens to multiple parties, up to 10. Note that the identity bank1
exists on the paladin1
instance,
therefore it must use that instance to send the transfer transction (.using(paladin1)
).
Bank1 exchanges Zeto tokens for ERC20 balances - withdraw¶
A participant like bank1
who has unspent Zeto tokens can call withdraw
on the Paladin Zeto domain to exchange them for ERC20 balances. Behind the scenes, the requested amount are "burnt" in the Zeto contract, and the corresponding ERC20 amount are released by the Zeto contract, by transferring to the requesting account.