Skip to content

Commit

Permalink
Merge pull request #6 from Uniswap/contract-split
Browse files Browse the repository at this point in the history
Break up logic into additional contracts
  • Loading branch information
0age authored Oct 30, 2024
2 parents 5683cee + ee27ad0 commit 72eb7a7
Show file tree
Hide file tree
Showing 13 changed files with 2,558 additions and 2,233 deletions.
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
> :warning: This is an early-stage contract under active development; it has not yet been properly tested, reviewed, or audited.
## Summary
The Compact is an ownerless ERC6909 contract that facilitates the voluntary formation (and, if necessary, eventual dissolution) of resource locks.
The Compact is an ownerless ERC6909 contract that facilitates the voluntary formation (and, if necessary, eventual dissolution) of reusable resource locks.

Resource locks are entered into by ERC20 or native token holders, called the **sponsor**. Once a resource lock has been established, sponsors can create a compact, or a commitment allowing interested parties to claim their tokens through an **arbiter** indicated by the sponsor that attests to the specified conditions of the compact having been met.
Resource locks are entered into by ERC20 or native token holders, called the _**sponsor**_. Once a resource lock has been established, sponsors can create a compact, or a commitment allowing interested parties to claim their tokens through an _**arbiter**_ indicated by the sponsor that attests to the specified conditions of the compact having been met.

Each resource lock is mediated by an **allocator**, tasked with attesting to the availability of the underlying token balances and preserving the balances required for the commitments they have attested to; in other words, an allocator ensures that sponsors do not "double-spend," transfer, or withdraw any token balances that are already committed to a specific compact.
Each resource lock is mediated by an _**allocator**_, tasked with attesting to the availability of the underlying token balances and preserving the balances required for the commitments they have attested to; in other words, an allocator ensures that sponsors do not "double-spend," transfer, or withdraw any token balances that are already committed to a specific compact.

Once a sponsor and their designated allocator have both committed to a compact, a **claimant** may then immediately perform the attached condition (such as delivering another token on some destination chain) and then claim the allocated tokens by initiating a call to the associated arbiter, which will verify and mediate the terms and conditions of the associated compact and relay the confirmation to process the claim.
Once a sponsor and their designated allocator have both committed to a compact, a _**claimant**_ may then immediately perform the attached condition (such as delivering another token on some destination chain) and then claim the allocated tokens by initiating a call to the associated arbiter, which will verify and mediate the terms and conditions of the associated compact and relay the confirmation to process the claim.

The Compact effectively "activates" any deposited tokens to be instantly spent or swapped across arbitrary, asynchronous environments as long as:
- the claimant is confident that the allocator is sound and will not leave the resource lock underallocated,
Expand Down Expand Up @@ -52,14 +52,14 @@ Once an allocator has been registered on a given chain for the first time, it wi
A given allocator only needs to be registered once per chain and can then be utilized by many different resource locks.

The allocator's primary function is to ensure that any resource locks it is assigned to are not "double-spent" — this entails ensuring that sufficient unallocated balance is available before cosigning on any requests to withdraw or transfer the balance or to sponsor a claim on that balance, and also ensuring that nonces are not reused.
The allocator's primary function is to ensure that any resource locks it is assigned to are not "double-spent" — this entails ensuring that sufficient unallocated balance is available before cosigning on any requests to withdraw or transfer the balance or to sponsor a claim on that balance, and also ensuring that nonces are not reused. Allocators can also call a `consume` method at any point to consume nonces so that they cannot be used again.

### 2) Deposit tokens
To enter into The Compact and create resource locks, a depositor begins by selecting for their four preferred properties for the lock:
- the underlying token held in the resource lock
- the allocator tasked with cosigning on claims against the resource locks and ensuring that the resource lock is not "double-spent" in any capacity, indicated by its registered allocator ID
- the "scope" of the resource lock (either spendable on any chain or limited to a single chain, with a default option of being spendable on any chain)
- the "reset period" for forceably exiting the lock and withdrawing the funds without the allocator's approval (one of eight preset values ranging from one second to thirty days, with a default option of ten minutes)
- the _**underlying token**_ held in the resource lock
- the _**allocator**_ tasked with cosigning on claims against the resource locks and ensuring that the resource lock is not "double-spent" in any capacity, indicated by its registered allocator ID
- the _**scope**_ of the resource lock (either spendable on any chain or limited to a single chain, with a default option of being spendable on any chain)
- the _**reset period**_ for forceably exiting the lock and withdrawing the funds without the allocator's approval (one of eight preset values ranging from one second to thirty days, with a default option of ten minutes)

Each unique combination of these four properties is represented by a fungible ERC6909 tokenID.

Expand All @@ -72,6 +72,8 @@ Depending on the selected properties of the resource lock, the number of tokens
- a non-payable deposit function that supplies an ERC20 token, amount, and associated Permit2 signature data (with sufficient allowance set on Permit2), and that specifies the allocator, the scope, the reset period, and the recipient of the 6909 tokens representing ownership of the lock (all of which are included as part of the Permit2 witness)
- a payable deposit function that supplies an array of tokens and amounts (including an optional native token followed by any number of ERC20 tokens), and associated Permit2 batch signature data (with sufficient allowance set on Permit2 for each ERC20 token), and that specifies the allocator, the scope, the reset period, and the recipient of the 6909 tokens representing ownership of each lock (all of which are included as part of the Permit2 witness).

There are also five `depositAndRegister` functions that simultaneously perform a deposit into a resource lock and register a compact (see section 3b).

> For ERC20 deposits, the amount of 6909 tokens minted is based on the change in the actual token balance held by The Compact after making the deposit, not the supplied amount; this enables support for making deposits of fee-on-transfer tokens where the fee is deducted from the recipient.
As long as allocators generally operate in an honest and reliable manner, this is the only direct interaction that end users will need to take; furthermore, in the case of the Permit2 deposit methods, the interaction can be made in a gasless fashion (where another party relays the signed message on behalf of the depositor).
Expand Down Expand Up @@ -178,9 +180,9 @@ To be considered valid, each compact must meet the following requirements:
Once this payload has been signed by both the sponsor and the allocator (or at least by one party if the other is the intended caller), a claim can be submitted against it by the designated arbiter using a wide variety of functions (104 to be exact) depending on the type of compact and the intended result.

### 3b) Submit a Compact Directly
Alternatively, the sponsor can register a compact (or group of compacts) by submitting a "claim hash" along with the typehash of the underlying compact (which maps to the EIP-712 message hash that would otherwise have been signed). Then, instead of supplying the signature of the sponsor as part of a claim, The Compact derives the claim hash based on the claim being submitted and attempts to locate a matching registered claim hash with the correct typehash. This flow supports more advanced functionality, such as sponsors without the ability to sign (like protocols or DAOs), smart wallet / EIP7702-enabled sponsors that have their own authorization or batching logic, and chained deposit & register operations.
Alternatively, the sponsor can register a compact (or group of compacts) by submitting a "claim hash" along with the typehash of the underlying compact (which maps to the EIP-712 message hash that would otherwise have been signed). Then, instead of supplying the signature of the sponsor as part of a claim, The Compact derives the claim hash based on the claim being submitted and attempts to locate a matching registered claim hash with the correct typehash. This flow supports more advanced functionality, such as sponsors without the ability to sign (like protocols or DAOs), smart wallet / EIP7702-enabled sponsors that have their own authorization or batching logic, and chained deposit & register operations. When registering a compact directly, a duration is either explicitly provided or inferred from the reset period on the corresponding deposit where available; the registered compact becomes inactive once that duration elapses.

> Note: once registered, a compact cannot be unregistered. The only way to definitively cancel a registered compact is by either having the allocator consume the attached nonce. Alternatively, the sponsor may perform a forced withdrawal to render the compact unclaimable until tokens are placed back into the resource lock.
> Note: once registered, a compact cannot be unregistered. The only way to definitively cancel a registered compact is by either having the allocator consume the attached nonce or waiting for the registered compact to expire. Alternatively, the sponsor may perform a forced withdrawal to render the compact unclaimable until tokens are placed back into the resource lock.
### 4) Submit a Claim
An arbiter takes a signed compact designated to them and uses it to submit a claim to The Compact.
Expand Down Expand Up @@ -242,10 +244,12 @@ struct SplitByIdComponent {
interface ICompactAllocatedTransferAndWithdrawal {
function allocatedTransfer(BasicTransfer calldata transfer) external returns (bool);
function allocatedWithdrawal(BasicTransfer calldata withdrawal) external returns (bool);
/* NOTE: these functions are currently unavailable; use SplitBatchTransfer for this functionality
function allocatedTransfer(SplitTransfer calldata transfer) external returns (bool);
function allocatedWithdrawal(SplitTransfer calldata withdrawal) external returns (bool);
function allocatedTransfer(BatchTransfer calldata transfer) external returns (bool);
function allocatedWithdrawal(BatchTransfer calldata withdrawal) external returns (bool);
*/
function allocatedTransfer(SplitBatchTransfer calldata transfer) external returns (bool);
function allocatedWithdrawal(SplitBatchTransfer calldata withdrawal) external returns (bool);
}
Expand Down Expand Up @@ -705,8 +709,9 @@ interface ICompactBatchMultichainClaims{

### 5. View Functions
In addition to standard ERC6909 view functions, The Compact includes the following view functions:
- `getForcedWithdrawalStatus` gives the current forced withdrawal status of a given account (either deactivated, pending, or activated) for a given resource lock, and the time at which it becomes active if it is currently pending
- `getLockDetails` gives the address of the underlying token, the address of the allocator, the reset period, and the scope (Multichain vs. Chain-specific) for a given resource lock
- `check` determines if a given nonce has been consumed for a given allocator (note that nonces are scoped to allocators, not sponsors)
- `DOMAIN_SEPARATOR` returns the hash of the EIP-712 domain data for the chain in question
- `getForcedWithdrawalStatus` gives the current forced withdrawal status of a given account (either deactivated, pending, or activated) for a given resource lock, and the time at which it becomes active if it is currently pending.
- `getLockDetails` gives the address of the underlying token, the address of the allocator, the reset period, and the scope (Multichain vs. Chain-specific) for a given resource lock.
- `getRegistrationStatus` gives the current compact registration status for a given sponsor, claim hash, and type hash and the time at which the registered compact is considered inactive.
- `check` determines if a given nonce has been consumed for a given allocator (note that nonces are scoped to allocators, not sponsors).
- `DOMAIN_SEPARATOR` returns the hash of the EIP-712 domain data for the chain in question.
- `name` returns the name of the contract.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ solc = '0.8.28'
evm_version='cancun'
via_ir = true
# optimizer_runs = 4_294_967_295
optimizer_runs = 50
optimizer_runs = 200
bytecode_hash = 'none'
src = "src"
out = "out"
Expand Down
70 changes: 35 additions & 35 deletions snapshots/TheCompactTest.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
{
"basicTransfer": "57278",
"basicWithdrawal": "60320",
"batchClaim": "112360",
"batchClaimRegisteredWithDeposit": "112360",
"batchClaimRegisteredWithDepositWithWitness": "113051",
"batchClaimWithWitness": "113045",
"batchDepositAndRegisterViaPermit2": "221954",
"batchDepositAndRegisterWithWitnessViaPermit2": "221932",
"claim": "57501",
"claimAndWithdraw": "73758",
"claimWithWitness": "59955",
"depositAndRegisterViaPermit2": "124302",
"depositBatchSingleERC20": "67822",
"depositBatchSingleNative": "28086",
"depositBatchViaPermit2NativeAndERC20": "129587",
"depositBatchViaPermit2SingleERC20": "104714",
"depositERC20AndURI": "67145",
"depositERC20Basic": "67152",
"depositERC20ViaPermit2AndURI": "98349",
"depositETHAndURI": "26769",
"depositETHBasic": "28288",
"qualifiedBatchClaim": "113731",
"qualifiedBatchClaimWithWitness": "113174",
"qualifiedClaim": "60773",
"qualifiedClaimWithWitness": "59336",
"qualifiedSplitBatchClaim": "141307",
"qualifiedSplitBatchClaimWithWitness": "141278",
"qualifiedSplitClaim": "87001",
"qualifiedSplitClaimWithWitness": "87324",
"basicTransfer": "56751",
"basicWithdrawal": "59775",
"batchClaim": "111809",
"batchClaimRegisteredWithDeposit": "111809",
"batchClaimRegisteredWithDepositWithWitness": "112505",
"batchClaimWithWitness": "112499",
"batchDepositAndRegisterViaPermit2": "221852",
"batchDepositAndRegisterWithWitnessViaPermit2": "221830",
"claim": "56934",
"claimAndWithdraw": "73203",
"claimWithWitness": "59399",
"depositAndRegisterViaPermit2": "124230",
"depositBatchSingleERC20": "67780",
"depositBatchSingleNative": "28083",
"depositBatchViaPermit2NativeAndERC20": "129518",
"depositBatchViaPermit2SingleERC20": "104645",
"depositERC20AndURI": "67073",
"depositERC20Basic": "67080",
"depositERC20ViaPermit2AndURI": "98277",
"depositETHAndURI": "26733",
"depositETHBasic": "28252",
"qualifiedBatchClaim": "113183",
"qualifiedBatchClaimWithWitness": "112625",
"qualifiedClaim": "60215",
"qualifiedClaimWithWitness": "58777",
"qualifiedSplitBatchClaim": "140849",
"qualifiedSplitBatchClaimWithWitness": "140819",
"qualifiedSplitClaim": "86458",
"qualifiedSplitClaimWithWitness": "86780",
"register": "25335",
"splitBatchClaim": "140794",
"splitBatchClaimWithWitness": "140731",
"splitBatchTransfer": "113570",
"splitBatchWithdrawal": "142818",
"splitClaim": "86927",
"splitClaimWithWitness": "86403"
"splitBatchClaim": "140333",
"splitBatchClaimWithWitness": "140275",
"splitBatchTransfer": "113034",
"splitBatchWithdrawal": "142264",
"splitClaim": "86381",
"splitClaimWithWitness": "85862"
}
Loading

0 comments on commit 72eb7a7

Please sign in to comment.