> For the complete documentation index, see [llms.txt](https://pingmexyz.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://pingmexyz.gitbook.io/docs/technical-resources/business-functions.md).

# Business Functions

The following is a matrix of all the possible flows of funds between a self-custodial wallet, lockbox and balance:

<table><thead><tr><th width="105.61328125">From/To</th><th>Wallet</th><th width="176.78515625">Lockbox</th><th>Balance</th></tr></thead><tbody><tr><td>Wallet</td><td>retrieve() from forwarder</td><td>send()</td><td>deposit()</td></tr><tr><td>Lockbox</td><td>reclaim() if sent from wallet</td><td>N/A</td><td>claim()<br>reclaim() (if sent from balance)</td></tr><tr><td>Balance</td><td>withdraw()</td><td>withdrawAndSend()</td><td>withdrawAndDeposit()</td></tr></tbody></table>

All but the Lockbox-to-Lockbox combination are relevant and included in this design.

### 1. createBalance()

Create a new zero-balance for new users.&#x20;

{% hint style="info" %}
To avoid error, call `isCommitmentExist()` as sanity check to ensure `balanceCommitment` does NOT already exist.&#x20;
{% endhint %}

**Parameters:**

* salt = hash(email + password)
* balanceCommitment (=hash(hash(email + password + currentSalt)))

**Behaviors:**

* If salt exists, validate balance exists, and commitment chain is well-defined.  The function is idempotent.
* Otherwise, validates balance does not exist, create new `identitySalt (=hash(email + password))` as currentSalt.
* Create zero balance, set `updateTime` as current time.
* In one embodiment, `createBalance()` is supported by 2FA server, which requires the new user to first click on a link sent by the server containing a secret, and thus verify ownership of the email account.

### 2. send()

Send funds from a sender's wallet into a temporary lockbox.&#x20;

{% hint style="info" %}
To avoid error, call `isLockboxAvailable()` as sanity check to ensure commitment not in use.
{% endhint %}

**Parameters:**

* amount
* duration (timeout duration. Recipient can claim the amount before timeout, after which the Sender can reclaim it back)
* commitment (double-hashed claim proof =hash(hash(hash(email + passphrase) + lockboxSalt))

**Behaviors:**

* Validate:
  * Amount is not too low (waste gas)&#x20;
  * Duration is not too low (frivolous)
  * Commitment is not zero
  * Lockbox does not always exist under the same commitment (duplicate)
* Records lockbox sender, amount, createTime and unlockTime
* Status initialized to Open
* In one embodiment, to support 2FA and non-repudiation, commitment was computed by sender as follows:
  * Sender sets recipient email, passphrase and optionally passphrase hint
  * Sender client submits email and passphrase-hint to layer-2 server
  * Layer-2 server creates random value R, then
    * Send lockboxSalt (=hash(R)) to sender
    * Stores the pending notification parameters (email, hint, R)
  * Sender client computes `hash(hash(email + passphrase + lockboxSalt))`, and call `send()` directly to blockchain
  * Sender requests server to send email to recipient, which includes URL with email, hint and R
  * When later the recipient uses the URL, the server will note the knowledge of R in the URL. That forms the basis of non-repudiation: the sender cannot claim his own deposit, then accuse the recipient of having done so, since the claim would have lacked the knowledge of R
* Under another embodiment, where 2FA and non-repudiation are not required or undesirable, commitment was computed by sender as follows:
  * Sender sets recipient email, passphrase and optionally passphrase hint
  * Sender client creates random value R, and computes `lockboxSalt (=hash(R))`
  * Sender client computes `hash(hash(email + passphrase + lockboxSalt))`, and call send() directly to blockchain
  * Sender client creates URL (or its QR code representation) including email, hint and lockboxSalt.  Note that R is missing in the URL
  * Sender sends or presents URL (or its QR code representation) directly to recipient
  * Since R does not exist in the URL, there is no non-repudiation

### 3. isLockboxAvailable(), getLockbox()

* `isLockboxAvailable()` returns true if no lockbox is found under the commitment.
* `getLockbox()` retrieves the details of a lockbox based on the provided lockbox commitment.

**Parameters:**

* commitment: the unique commitment of the lockbox. This hash is derived from a user’s email, passphrase, and an additional lockbox salt, and is used to uniquely identify each lockbox.

**Behaviors:**&#x20;

* Validates:
  * lockbox commitment is correct, by checking `lockboxes[hash(proof)].createTime != 0`
* If the lockbox exists, return all the lockbox details
* If it does not exist, the function will revert with an error

### 4. claim()

Recipient moves funds from a lockbox into balance.&#x20;

{% hint style="info" %}
For new user, they must first call `isCommitmentExist()` to ensure balanceCommitment does NOT already exist.&#x20;

For existing users, they must first call `getBalance()` to ensure it is valid.
{% endhint %}

**Parameters:**

* `proof = hash(email + passphrase + lockboxSalt)`
* `salt = hash(email + password)`
* `balanceCommitment (=hash(hash(email + password + currentSalt)))`

**Behaviors:**&#x20;

* Validates:
  * lockbox commitment is correct, by checking `lockboxes[hash(proof)].createTime != 0`
  * lockbox is still Open
  * lockbox is not expired
  * amount is not too low (sanity check)
* If salt exists, validate balance exists. CurrentSalt can be obtained by calling `getCurrentSalt(salt)`
* Otherwise, validates balance does not exist, create new `identitySalt (=hash(email + password))` as currentSalt
* Increase balance with box amount, set updateTime as current time, empty box, and set to claimed
* Note that lockboxSalt was either given by the sender directly in the URL (under no-2FA scenario), or computed by the recipient `(lockboxSalt = hash(R))` based on R given in the URL (under the 2FA scenario)
* In one embodiment, the recipient can elect to store their password in local device keystore, secured by biometrics or system passwords

### 5. deposit(), isCommitmntExist()

* `isCommitmentExist()` verifies that it is an existing commitment on chain, current or previous.
* `deposit()` funds to an existing pseudonymous balance identified by a commitment.

**Parameters:**

* amount
* commitment: The (possibly outdated) balance commitment. The contract will resolve to the current commitment using the commitment chain.

**Behaviours:**

* Validates:
  * amount must be at least MIN\_AMOUNT
  * The resolved commitment must correspond to an existing balance
* Resolve to the current commitment on the commitment chain
* Credits amount to the resolved balance, and set `updateTime` as current time
* Reverts if no valid balance is found

### 6. getBalance(), retrieve()

* `getBalance()` looks up their existing balance. Layer-2 server user flow can call this to provide visual confirmation to the user before and after their new funds are claimed.
* `retrieve()` will check the balance on forwarder, and instruct it to release any funds held.

**Parameters:**

* `commitment (=hash(hash(email + passphrase + currentIdentitySalt)))`

**Behavior:**

* returns the current balance
* Checks if Forwarder for the commitment exists. If yes, it signals it to release all its held funds, so the balance is updated

### 7. withdraw()

Withdraw funds from a balance commitment to a real wallet (on-chain address).

{% hint style="info" %}
Caller must first call `isCommitmentExist()` as sanity check to ensure nextCommitment does NOT already exist.
{% endhint %}

**Parameters:**

* proof = hash(email + password + currentSalt)
* salt = hash(email + nextPassword)
* amount
* nextCommitment = hash(hash(email + nextPassword + nextSalt))
* recipient = address of recipient

**Behaviors:**

* Validates
  * proof, by comparing `hash(proof)` to an existing balance’s commitment
  * amount is at least MIN\_AMOUNT and no more than available balance
  * salt exists
  * nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one
  * recipient address is not address(0) (burns)
* Increment salt iteration
* Shift reduced balance to `nextCommitment`, and set `updateTime` as current time
* Update commitment chain for the withdrawer
* Withdraw to recipient address

### 8. withdrawAndSend()

Withdraw funds from a balance commitment into a lockbox, allowing the recipient to claim it pseudonymously.

{% hint style="info" %}
Caller must first call `isCommitmentExist()` as sanity check to ensure nextCommitment does NOT already exist.

And call `isLockboxAvailable()` as sanity check to ensure commitment is not in use.
{% endhint %}

**Parameters:**

* proof = hash(email + password + currentSalt)
* salt = hash(email + nextPassword)
* amount
* nextCommitment = hash(hash(email + nextPassword + nextSalt))
* duration = number of seconds the lockbox remains claimable
* commitment = double-hashed lockbox key (used by the recipient to claim)

**Behavior:**

* Validates
  * proof, by comparing `hash(proof)` to an existing balance’s commitment
  * amount is at least MIN\_AMOUNT and no more than available balance
  * salt exists
  * nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one
  * duration is at least MIN\_DURATION
* Increment salt iteration
* Shift reduced balance to `nextCommitment`, and set `updateTime` as current time
* Update commitment chain for the withdrawer
* Creates a new lockbox with:
  * amount
  * sender = address(0)
  * senderCommitment = nextCommitment
  * unlockTime = now + duration
  * status = Open

### 9. withdrawAndDeposit()

Withdraw funds from a balance commitment to another pseudonymous balance (rebind).

{% hint style="info" %}
Caller must first call `isCommitmentExist()` as sanity check to ensure nextCommitment does NOT already exist.

And call `isCommitmentExist()` to ensure recipient’s commitment exists.
{% endhint %}

**Parameters:**

* proof = hash(email + password + currentSalt)
* salt = hash(email + nextPassword)
* amount
* nextCommitment = hash(hash(email + nextPassword + nextSalt))
* commitment of recipient (along the chain of commitments) to receive the deposit

**Behavior:**

* Validates
  * proof, by comparing hash(proof) to an existing balance’s commitment
  * amount is at least MIN\_AMOUNT and no more than available balance
  * salt exists
  * nextCommitment does not exist on commitment chain nor balances, and it is not equal to the current one
* Increment salt iteration
* Shift reduced balance to nextCommitment, and set updateTime as current time
* Update commitment chain for the withdrawer
* Resolve to current commitment using commitment chain for the deposit
* Increases the balance of resolved commitment by amount

### 10. changPassword()

Recipient changes password for an existing balance.

{% hint style="info" %}
Caller must first call `isCommitmentExist()` as sanity check to ensure nextCommitment does NOT already exist.
{% endhint %}

**Parameters:**

* proof = hash(email + password + currentSalt)
* nextSalt = hash(email + nextPassword)
* nextCommitment = hash(hash(email + nextPassword + nextSalt))

**Behaviors:**

* Validates
  * proof, by comparing `hash(proof)` to an existing balance’s commitment
  * `nextSalt` does not exist (i.e. passwords cannot be reused)
  * `nextCommitment` does not exist on commitment chain nor balances
* Save new salt
* Shift balance
* Update commitment chain

### 11. reclaim()

Reclaim funds from a lockbox after it has passed its unlock time. It can transfer the funds back to the sender’s wallet if it was sent by `send()`, or to the sender’s balance if it was sent by `withdrawAndSend()`.

{% hint style="info" %}
To avoid error, first call `getLockbox()` as sanity check to ensure lockbox exists, and verify its expiry status before electing to reclaim it.
{% endhint %}

**Parameters:**

* commitment (double-hashed claim proof =hash(hash(hash(email + passphrase) + lockboxSalt))

**Behaviors:**&#x20;

* Validates
  * commitment points to a lockbox
  * status is still Open
  * unlockTime has expired
  * amount is not too low (sanity check)
* Sends funds back to sender’s wallet, or to balance under senderCommitment, as recorded at the time when the Lockbox was created
* Lockbox amount set to 0, and status to Reclaimed
* Note that knowledge of proof is not required. As long as the sender (or Layer-2 server) has the commitment, it can trigger the `reclaim()`
