Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

noto: add "lock" functionality #483

Open
wants to merge 56 commits into
base: main
Choose a base branch
from
Open

noto: add "lock" functionality #483

wants to merge 56 commits into from

Conversation

awrichar
Copy link
Contributor

@awrichar awrichar commented Dec 17, 2024

Resolves #455
In a chain with #490

Allows an owner to "lock" some portion of value such that it cannot be spent except when specific conditions are met. This provides a programmable way to set aside some value for a specific purpose and to prepare value transfers to be completed by other public smart contracts (such as escrow and swap contracts).

New methods on Noto private ABI:

  • lock(uint256 amount, bytes calldata data) - lock some amount of my Noto value and assign it a unique lock ID
  • unlock(bytes32 lockId, string calldata from, UnlockRecipient[] calldata recipients, bytes calldata data) - unlock some value that I previously locked, and send it to one or more recipients
  • prepareUnlock(bytes32 lockId, string calldata from, UnlockRecipient[] calldata recipients, bytes calldata data) - prepare to unlock some value that I previously locked, and record the prepared transition on the base ledger (but do not actually unlock)
  • delegateLock(bytes32 lockId, address delegate, bytes calldata data) - delegate to another address that will be able to execute the prepared unlock operation (requires prepareUnlock to have been called)

Where the UnlockRecipient struct used here is defined as:

struct UnlockRecipient {
    string to;
    uint256 amount;
}

Some notes about these methods:

  • It's possible some of the methods could be combined. You could make a case for lock + prepareUnlock or prepareUnlock + delegateLock or even lock + prepareUnlock + delegateLock, but for the moment I've left them all as separate transactions.
  • I've also explored whether it should be possible to prepare multiple unlock possibilities at one time, but for now have settled on having at most one prepared unlock operation per lock.
  • I did make it possible to unlock to multiple recipients in a single call (dividing it up as long as the sum is less than the total amount locked), and to unlock multiple times (you may keep unlocking value with separate calls until the entire value has been unlocked). We will need to decide if this flexibility is useful or if any of it is over-complicated. It also begs the question as to whether we need additional transfer variants for multiple recipients or batching, to match what is now possible with locking.
  • Although unlock by default only allows the original lock owner to unlock the states, more complex flows can be implemented via Pente hooks. Particularly something like Exploration on contracts for NotoDepositWithdraw #485 should be possible, where one party locks some value and spawns a separate contract to track it (such as an ERC20), and then various other parties are able to claim portions of the locked value by presenting proofs of their entitlement to unlock it.

The unlock method on the Noto public ABI can be called via the notary, or may be called directly via a public transaction if an unlock operation has been prepared and delegated.

Domain receipts have now been implemented for Noto, allowing easy extraction of the following:

  • All input/output/prepared states, broken down by regular coins vs. locked coins
  • The generated lock ID (for all lock/unlock transactions)
  • A pre-encoded unlock function call that can be used to run a prepared unlock (for prepareUnlock only)

This PR also removes the "NotoSelfSubmit" variant. This contract was not fully proved out, and it doesn't seem useful to continue adding features to it at this time. We can always bring it back in the future if needed.

@jimthematrix
Copy link
Contributor

should approveUnlock() be called delegateLock() to better reflect what it does?

@jimthematrix
Copy link
Contributor

another observation after some further discussions with @awrichar is whether the unlock functions can just be the regular transferWithApproval() calls.

Allows a domain to directly trigger a new transaction, such as in response to
an event.

Signed-off-by: Andrew Richardson <[email protected]>
Change Noto to ensure notary name is fully qualified during deploy, and to
check on contract init if the current node is the notary.

Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
Allows a domain to look up specific states, regardless of whether they've been
spent or not.

Signed-off-by: Andrew Richardson <[email protected]>
Copy link

This PR is stale because it has been open 30 days with no activity.

@github-actions github-actions bot added the stale label Jan 17, 2025
Signed-off-by: Andrew Richardson <[email protected]>
This variant isn't fully proved out, and may begin causing confusion.
We can bring it back at the time we're able to work through all of the
flows in detail.

Signed-off-by: Andrew Richardson <[email protected]>
Allows an owner to "lock" some portion of value such that it cannot be
spent except when specific conditions are met.

Signed-off-by: Andrew Richardson <[email protected]>
Currently you can only unlock to return the value to yourself (additional
work needed to allow specifying and executing a transfer).

Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
Also gather two separate sender signatures for transfer + lock, so that
they can be emitted separately in the two blockchain events.

Signed-off-by: Andrew Richardson <[email protected]>
Use shorter names "restrictMint" and "allowBurn".

Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
Rather than storing all input/outputs states on the smart contract, store an
EIP-712 hash of the prepared unlock. Allow extracting the input/output states
from the state receipt (as info/read states) so that they can be passed in to
the final "unlockWithApproval" transaction.

Signed-off-by: Andrew Richardson <[email protected]>
Get states by ID, regardless of whether they are available.

Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
This helper is actually making the code more confusing.

Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
Signed-off-by: Andrew Richardson <[email protected]>
This makes it easier to separate locked/unlocked states.

Signed-off-by: Andrew Richardson <[email protected]>
Copy link

This PR is stale because it has been open 30 days with no activity.

@github-actions github-actions bot added the stale label Jan 19, 2025
@github-actions github-actions bot removed the stale label Jan 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add locking functionality to Noto
2 participants