mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-02-18 12:23:19 +00:00
251 lines
9.5 KiB
Markdown
251 lines
9.5 KiB
Markdown
|
|
This tutorial walks through native token transfers between public and private accounts using the Authenticated-Transfers program. You will create and initialize accounts, fund them with the Pinata program, and run transfers across different privacy combinations. By the end, you will have practiced:
|
|||
|
|
1. Public account creation and initialization.
|
|||
|
|
2. Account funding through the Pinata program.
|
|||
|
|
3. Native token transfers between public accounts.
|
|||
|
|
4. Private account creation.
|
|||
|
|
5. Native token transfer from a public account to a private account.
|
|||
|
|
6. Native token transfer from a public account to a private account owned by someone else.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
The CLI provides commands to manage accounts. Run `wallet account` to see the options available:
|
|||
|
|
```bash
|
|||
|
|
Commands:
|
|||
|
|
get Get account data
|
|||
|
|
new Produce new public or private account
|
|||
|
|
sync-private Sync private accounts
|
|||
|
|
help Print this message or the help of the given subcommand(s)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 1. Public account creation and initialization
|
|||
|
|
> [!Important]
|
|||
|
|
> Public accounts live on-chain and are identified by a 32-byte Account ID. Running `wallet account new public` generates a fresh keypair for the signature scheme used in LEZ.
|
|||
|
|
> The account ID is derived from the public key, and the private key signs transactions and authorizes program executions.
|
|||
|
|
> The CLI can create both public and private accounts.
|
|||
|
|
|
|||
|
|
### a. New public account creation
|
|||
|
|
```bash
|
|||
|
|
wallet account new public
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Generated new account with account_id Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
```
|
|||
|
|
> [!Tip]
|
|||
|
|
> Save this account ID. You will use it in later commands.
|
|||
|
|
|
|||
|
|
### b. Account initialization
|
|||
|
|
|
|||
|
|
To query the account’s current status, run:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Replace the id with yours
|
|||
|
|
wallet account get --account-id Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account is Uninitialized
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
In this example, we initialize the account for the authenticated-transfer program, which manages native token transfers and enforces authenticated debits.
|
|||
|
|
|
|||
|
|
1. Initialize the account:
|
|||
|
|
```bash
|
|||
|
|
# This command submits a public transaction executing the `init` function of the
|
|||
|
|
# authenticated-transfer program. The wallet polls the sequencer until the
|
|||
|
|
# transaction is included in a block, which may take several seconds.
|
|||
|
|
wallet auth-transfer init --account-id Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. Check the updated account status:
|
|||
|
|
```bash
|
|||
|
|
wallet account get --account-id Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":0}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!NOTE]
|
|||
|
|
> New accounts start uninitialized, meaning no program owns them yet. Any program may claim an uninitialized account; once claimed, that program owns it.
|
|||
|
|
> Owned accounts can only be modified through executions of the owning program. The only exception is native-token credits: any program may credit native tokens to any account.
|
|||
|
|
> Debiting native tokens must always be performed by the owning program.
|
|||
|
|
|
|||
|
|
## 2. Account funding through the Piñata program
|
|||
|
|
Now that the account is initialized under the authenticated-tansfer program, fund it using the testnet Piñata program.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Replace with your id
|
|||
|
|
wallet pinata claim --to Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
After the claim succeeds, the account is funded:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet account get --account-id Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":150}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. Native token transfers between public accounts
|
|||
|
|
LEZ includes a program for managing native tokens. Run `wallet auth-transfer` to see the available commands:
|
|||
|
|
```bash
|
|||
|
|
Commands:
|
|||
|
|
init Initialize account under the authenticated-transfer program
|
|||
|
|
send Send native tokens from one account to another with variable privacy
|
|||
|
|
help Print this message or the help of the given subcommand(s)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
We already used `init`. Now use `send` to execute a transfer.
|
|||
|
|
|
|||
|
|
### a. Create a recipient account
|
|||
|
|
```bash
|
|||
|
|
wallet account new public
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Generated new account with account_id Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!NOTE]
|
|||
|
|
> The new account is uninitialized. The authenticated-transfer program will claim any uninitialized account used in a transfer, so manual initialization isn’t required.
|
|||
|
|
|
|||
|
|
### b. Send 37 tokens to the new account
|
|||
|
|
```bash
|
|||
|
|
wallet auth-transfer send \
|
|||
|
|
--from Public/9ypzv6GGr3fwsgxY7EZezg5rz6zj52DPCkmf1vVujEiJ \
|
|||
|
|
--to Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS \
|
|||
|
|
--amount 37
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### c. Check both accounts
|
|||
|
|
```bash
|
|||
|
|
# Sender account (use your sender ID)
|
|||
|
|
wallet account get --account-id Public/HrA8TVjBS8UVf9akV7LRhyh6k4c7F6PS7PvqgtPmKAT8
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":113}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Recipient account
|
|||
|
|
wallet account get --account-id Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":37}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. Private account creation
|
|||
|
|
|
|||
|
|
> [!Important]
|
|||
|
|
> Private accounts are structurally identical to public accounts, but their values are stored off-chain. On-chain, only a 32-byte commitment is recorded.
|
|||
|
|
> Transactions include encrypted private values so the owner can recover them, and the decryption keys are never shared.
|
|||
|
|
> Private accounts use two keypairs: nullifier keys for privacy-preserving executions and viewing keys for encrypting and decrypting values.
|
|||
|
|
> The private account ID is derived from the nullifier public key.
|
|||
|
|
> Private accounts can be initialized by anyone, but once initialized they can only be modified by the owner’s keys.
|
|||
|
|
> Updates include a new commitment and a nullifier for the old state, which prevents linkage between versions.
|
|||
|
|
|
|||
|
|
### a. Create a private account
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet account new private
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Generated new account with account_id Private/HacPU3hakLYzWtSqUPw6TUr8fqoMieVWovsUR6sJf7cL
|
|||
|
|
With npk e6366f79d026c8bd64ae6b3d601f0506832ec682ab54897f205fffe64ec0d951
|
|||
|
|
With ipk 02ddc96d0eb56e00ce14994cfdaec5ae1f76244180a919545983156e3519940a17
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!Tip]
|
|||
|
|
> Focus on the account ID for now. The `npk` and `ipk` values are stored locally and used to build privacy-preserving transactions. The private account ID is derived from `npk`.
|
|||
|
|
|
|||
|
|
Just like public accounts, new private accounts start out uninitialized:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet account get --account-id Private/HacPU3hakLYzWtSqUPw6TUr8fqoMieVWovsUR6sJf7cL
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account is Uninitialized
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!Important]
|
|||
|
|
> Private accounts are never visible to the network. They exist only in your local wallet storage.
|
|||
|
|
|
|||
|
|
## 5. Native token transfer from a public account to a private account
|
|||
|
|
|
|||
|
|
> [!Important]
|
|||
|
|
> Sending tokens to an uninitialized private account causes the authenticated-transfer program to claim it, just like with public accounts. Program logic is the same regardless of account type.
|
|||
|
|
|
|||
|
|
### a. Send 17 tokens to the private account
|
|||
|
|
|
|||
|
|
> [!Note]
|
|||
|
|
> The syntax matches public-to-public transfers, but the recipient is a private ID. This runs locally, generates a proof, and submits it to the sequencer. It may take 30 seconds to 4 minutes.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet auth-transfer send \
|
|||
|
|
--from Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS \
|
|||
|
|
--to Private/HacPU3hakLYzWtSqUPw6TUr8fqoMieVWovsUR6sJf7cL \
|
|||
|
|
--amount 17
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### b. Check both accounts
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Public sender account
|
|||
|
|
wallet account get --account-id Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":20}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Private recipient account
|
|||
|
|
wallet account get --account-id Private/HacPU3hakLYzWtSqUPw6TUr8fqoMieVWovsUR6sJf7cL
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Account owned by authenticated-transfer program
|
|||
|
|
{"balance":17}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!Note]
|
|||
|
|
> The last command does not query the network. It works offline because private account data is stored locally. Other users cannot read your private balances.
|
|||
|
|
|
|||
|
|
> [!Caution]
|
|||
|
|
> Private accounts can only be modified by their owner’s keys. The exception is initialization: any user can initialize an uninitialized private account. This enables transfers to a private account owned by someone else, as long as that account is uninitialized.
|
|||
|
|
|
|||
|
|
## 6. Native token transfer from a public account to a private account owned by someone else
|
|||
|
|
|
|||
|
|
> [!Important]
|
|||
|
|
> We’ll simulate transferring to someone else by creating a new private account we own and treating it as if it belonged to another user.
|
|||
|
|
|
|||
|
|
### a. Create a new uninitialized private account
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet account new private
|
|||
|
|
|
|||
|
|
# Output:
|
|||
|
|
Generated new account with account_id Private/AukXPRBmrYVqoqEW2HTs7N3hvTn3qdNFDcxDHVr5hMm5
|
|||
|
|
With npk 0c95ebc4b3830f53da77bb0b80a276a776cdcf6410932acc718dcdb3f788a00e
|
|||
|
|
With ipk 039fd12a3674a880d3e917804129141e4170d419d1f9e28a3dcf979c1f2369cb72
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!Tip]
|
|||
|
|
> Ignore the private account ID here and use the `npk` and `ipk` values to send to a foreign private account.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
wallet auth-transfer send \
|
|||
|
|
--from Public/Ev1JprP9BmhbFVQyBcbznU8bAXcwrzwRoPTetXdQPAWS \
|
|||
|
|
--to-npk 0c95ebc4b3830f53da77bb0b80a276a776cdcf6410932acc718dcdb3f788a00e \
|
|||
|
|
--to-ipk 039fd12a3674a880d3e917804129141e4170d419d1f9e28a3dcf979c1f2369cb72 \
|
|||
|
|
--amount 3
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!Warning]
|
|||
|
|
> This command creates a privacy-preserving transaction, which may take a few minutes. The updated values are encrypted and included in the transaction.
|
|||
|
|
> Once accepted, the recipient must run `wallet account sync-private` to scan the chain for their encrypted updates and refresh local state.
|
|||
|
|
|
|||
|
|
> [!Note]
|
|||
|
|
> You have seen transfers between two public accounts and from a public sender to a private recipient. Transfers from a private sender, whether to a public account or to another private account, follow the same pattern.
|