> 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()`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://pingmexyz.gitbook.io/docs/technical-resources/business-functions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
