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

Intent-based settlement Settler #227

Merged
merged 24 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
584e4d6
WIP: intent-based settlement Settler
duncancmt Aug 28, 2024
1f6ebb8
WIP: specialize `SettlerIntent` on Sepolia
duncancmt Aug 28, 2024
f89f399
WIP: specialize `SettlerIntent` on all other chains
duncancmt Aug 28, 2024
ac2d52a
`forge fmt`
duncancmt Aug 28, 2024
fe85e8f
Forbid proportional-sell behavior for Intent Settlers
duncancmt Aug 29, 2024
50d37fc
`CHANGELOG.md`
duncancmt Aug 29, 2024
238c20b
Add intents Settler info to `README.md`
duncancmt Aug 29, 2024
5b5654f
Merge branch 'master' into dcmt/intent
duncancmt Dec 10, 2024
6583040
Remove duplicate external function
duncancmt Dec 10, 2024
43a7591
Merge branch 'dcmt/etherscan-woes' into dcmt/intent
duncancmt Dec 12, 2024
0a66f2c
Merge branch 'dcmt/gnosis' into dcmt/intent
duncancmt Dec 12, 2024
350a7fb
Merge branch 'master' into dcmt/intent
duncancmt Dec 12, 2024
8c768bf
Merge branch 'master' into dcmt/intent
duncancmt Dec 14, 2024
bc686ae
Merge branch 'master' into dcmt/intent
duncancmt Dec 18, 2024
ebbbe46
Address audit finding: enforce mandatory slippage check for intents
duncancmt Jan 28, 2025
7650fd5
`forge fmt`
duncancmt Jan 28, 2025
60a2dde
Update snaps
duncancmt Jan 28, 2025
7f42532
Merge branch 'master' into dcmt/intent
duncancmt Jan 28, 2025
ad20b61
Update snaps
duncancmt Jan 28, 2025
456b29d
Add final Dedaub audit report for the "intent" feature
duncancmt Jan 29, 2025
6227a53
Merge branch 'dcmt/intent' into dcmt/intent
duncancmt Jan 29, 2025
650825c
Merge branch 'master' into dcmt/intent
duncancmt Jan 29, 2025
c1232e7
Update snaps
duncancmt Jan 29, 2025
cf98369
Move unreleased change to the correct position in the `CHANGELOG.md`
duncancmt Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ Master list of UniV3 forks:
* Add UniswapV3 UniV3 fork to Gnosis
* Add SushiswapV3 UniV3 fork to Gnosis
* Add Swapr UniV3 (Algebra style) fork to Gnosis
* Create new Settler version for intent-based settlement -- the taker only signs
the slippage, not the actions
* This is now `tokenId` 4

## 2024-10-08

Expand Down
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ ERC721-compatible NFT. To find the address of the most recent `Settler`
deployment, call `function ownerOf(uint256 tokenId) external view returns (address)`
with the `tokenId` set to the number of the feature that you wish to query. For
taker-submitted flows, the feature number is probably 2 unless something major
changed and nobody updated this document. Likewise, for gasless/metatransaction
flows, the feature number is probably 3. A reverting response indicates that
`Settler` is paused and you should not interact. Do not hardcode any `Settler`
address in your integration. _**ALWAYS**_ query the deployer/registry for the
address of the most recent `Settler` contract before building or signing a
transaction, metatransaction, or order.
changed and nobody updated this document. For gasless/metatransaction flows, the
feature number is probably 3. For intents, the feature number is probably 4. A
reverting response indicates that `Settler` is paused and you should not
interact. Do not hardcode any `Settler` address in your
integration. _**ALWAYS**_ query the deployer/registry for the address of the
most recent `Settler` contract before building or signing a transaction,
metatransaction, or order.

### 0x API dwell time

Expand Down Expand Up @@ -154,6 +155,7 @@ import { createPublicClient, http, parseAbi } from 'viem';
const tokenDescriptions = {
2: "taker submitted",
3: "metatransaction",
4: "intents",
};

const deployerAbi = parseAbi([
Expand Down Expand Up @@ -212,6 +214,7 @@ const {ethers} = require("ethers");
const tokenDescriptions = {
2: "taker submitted",
3: "metatransaction",
4: "intents",
};

const deployerAbi = [
Expand Down Expand Up @@ -301,8 +304,12 @@ async fn main() -> Result<()> {
let provider = ProviderBuilder::new().on_http(env::var("RPC_URL")?.parse()?);
let block_id = BlockId::number(provider.get_block_number().await?);

let token_ids = vec![2, 3];
let token_descriptions = HashMap::from([(2, "taker submitted"), (3, "metatransaction")]);
let token_ids = vec![2, 3, 4];
let token_descriptions = HashMap::from([
(2, "taker submitted"),
(3, "metatransaction"),
(4, "intents")
]);

for token_id in token_ids.iter() {
{
Expand Down Expand Up @@ -385,6 +392,7 @@ deployer_address = "0x00000000000004533Fe15556B1E086BB1A72cEae"
token_descriptions = {
2: "taker submitted",
3: "metatransaction",
4: "intents",
}

deployer_abi = [
Expand Down Expand Up @@ -462,6 +470,7 @@ declare -r deployer='0x00000000000004533Fe15556B1E086BB1A72cEae'
declare -A token_descriptions
token_descriptions[2]='taker submitted'
token_descriptions[3]='metatransaction'
token_descriptions[4]='intents'
declare -r -A token_descriptions

declare -r -a function_signatures=('prev(uint128)(address)' 'ownerOf(uint256)(address)' 'next(uint128)(address)')
Expand Down Expand Up @@ -1251,10 +1260,10 @@ from this document.
![Click on "Connect to Web3"](img/pause6.png?raw=true)

9. Enter the "feature" number in the text box. This is probably 2 for
taker-submitted for 3 for gasless/metatransaction, unless something major has
changed and nobody bothered to update this document.
taker-submitted, 3 for gasless/metatransaction, or 4 for intents, unless
something major has changed and nobody bothered to update this document.

![Enter the "feature" number (2 or 3) in the text box](img/pause7.png?raw=true)
![Enter the "feature" number (2, 3, or 4) in the text box](img/pause7.png?raw=true)

10. Click "Write" and confirm the transaction in your wallet. You have _really_ ruined everybody's day :+1:

Expand Down
7 changes: 3 additions & 4 deletions src/Settler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ abstract contract Settler is Permit2PaymentTakerSubmitted, SettlerBase {
using UnsafeMath for uint256;
using CalldataDecoder for bytes[];

// When/if you change this, you must make corresponding changes to
// `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set
// `constructor_args`.
constructor(bytes20 gitCommit) SettlerBase(gitCommit, 2) {}
function _tokenId() internal pure override returns (uint256) {
return 2;
}

function _hasMetaTxn() internal pure override returns (bool) {
return false;
Expand Down
7 changes: 7 additions & 0 deletions src/SettlerAbstract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ abstract contract SettlerAbstract is Permit2PaymentAbstract {
"SlippageAndActions(address recipient,address buyToken,uint256 minAmountOut,bytes[] actions)";
bytes32 internal constant SLIPPAGE_AND_ACTIONS_TYPEHASH =
0x615e8d716cef7295e75dd3f1f10d679914ad6d7759e8e9459f0109ef75241701;
// Permit2 Witness for intents
string internal constant SLIPPAGE_TYPE = "Slippage(address recipient,address buyToken,uint256 minAmountOut)";
bytes32 internal constant SLIPPAGE_TYPEHASH = 0xdc83993a2ffc65b01b71ed08790b6e39c5c55d76937b62a3b5085b02071f1259;

uint256 internal constant BASIS = 10_000;
IERC20 internal constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);

constructor() {
assert(SLIPPAGE_AND_ACTIONS_TYPEHASH == keccak256(bytes(SLIPPAGE_AND_ACTIONS_TYPE)));
assert(SLIPPAGE_TYPEHASH == keccak256(bytes(SLIPPAGE_TYPE)));
}

function _hasMetaTxn() internal pure virtual returns (bool);

function _tokenId() internal pure virtual returns (uint256);

function _dispatch(uint256 i, uint256 action, bytes calldata data) internal virtual returns (bool);
}
7 changes: 5 additions & 2 deletions src/SettlerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ abstract contract SettlerBase is Basic, RfqOrderSettlement, UniswapV3Fork, Unisw

event GitCommit(bytes20 indexed);

constructor(bytes20 gitCommit, uint256 tokenId) {
// When/if you change this, you must make corresponding changes to
// `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set
// `constructor_args`.
constructor(bytes20 gitCommit) {
if (block.chainid != 31337) {
emit GitCommit(gitCommit);
assert(IERC721Owner(0x00000000000004533Fe15556B1E086BB1A72cEae).ownerOf(tokenId) == address(this));
assert(IERC721Owner(0x00000000000004533Fe15556B1E086BB1A72cEae).ownerOf(_tokenId()) == address(this));
} else {
assert(gitCommit == bytes20(0));
}
Expand Down
72 changes: 72 additions & 0 deletions src/SettlerIntent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {SettlerAbstract} from "./SettlerAbstract.sol";
import {SettlerBase} from "./SettlerBase.sol";
import {SettlerMetaTxn} from "./SettlerMetaTxn.sol";

import {Permit2PaymentAbstract} from "./core/Permit2PaymentAbstract.sol";
import {Permit2PaymentIntent, Permit2PaymentMetaTxn, Permit2Payment} from "./core/Permit2Payment.sol";

import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol";

abstract contract SettlerIntent is Permit2PaymentIntent, SettlerMetaTxn {
function _tokenId() internal pure virtual override(SettlerAbstract, SettlerMetaTxn) returns (uint256) {
return 4;
}

function _msgSender()
internal
view
virtual
// Solidity inheritance is so stupid
override(Permit2PaymentMetaTxn, SettlerMetaTxn)
returns (address)
{
return super._msgSender();
}

function _witnessTypeSuffix()
internal
pure
virtual
// Solidity inheritance is so stupid
override(Permit2PaymentMetaTxn, Permit2PaymentIntent)
returns (string memory)
{
return super._witnessTypeSuffix();
}

function _hashSlippage(AllowedSlippage calldata slippage) internal pure returns (bytes32 result) {
// This function does not check for or clean any dirty bits that might
// exist in `slippage`. We assume that `slippage` will be used elsewhere
// in this context and that if there are dirty bits it will result in a
// revert later.
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(ptr, SLIPPAGE_TYPEHASH)
calldatacopy(add(ptr, 0x20), slippage, 0x60)
result := keccak256(ptr, 0x80)
}
}

function executeMetaTxn(
AllowedSlippage calldata slippage,
bytes[] calldata actions,
bytes32, /* zid & affiliate */
address msgSender,
bytes calldata sig
) public virtual override metaTx(msgSender, _hashSlippage(slippage)) returns (bool) {
return _executeMetaTxn(slippage, actions, sig);
}

function _permitToSellAmount(ISignatureTransfer.PermitTransferFrom memory permit)
internal
pure
virtual
override(Permit2PaymentAbstract, Permit2PaymentMetaTxn)
returns (uint256 sellAmount)
{
sellAmount = permit.permitted.amount;
}
}
28 changes: 17 additions & 11 deletions src/SettlerMetaTxn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ abstract contract SettlerMetaTxn is Permit2PaymentMetaTxn, SettlerBase {
using UnsafeMath for uint256;
using CalldataDecoder for bytes[];

// When/if you change this, you must make corresponding changes to
// `sh/deploy_new_chain.sh` and 'sh/common_deploy_settler.sh' to set
// `constructor_args`.
constructor(bytes20 gitCommit) SettlerBase(gitCommit, 3) {}
function _tokenId() internal pure virtual override returns (uint256) {
return 3;
}

function _hasMetaTxn() internal pure override returns (bool) {
return true;
Expand Down Expand Up @@ -122,13 +121,10 @@ abstract contract SettlerMetaTxn is Permit2PaymentMetaTxn, SettlerBase {
return true;
}

function executeMetaTxn(
AllowedSlippage calldata slippage,
bytes[] calldata actions,
bytes32, /* zid & affiliate */
address msgSender,
bytes calldata sig
) public metaTx(msgSender, _hashActionsAndSlippage(actions, slippage)) returns (bool) {
function _executeMetaTxn(AllowedSlippage calldata slippage, bytes[] calldata actions, bytes calldata sig)
internal
returns (bool)
{
require(actions.length != 0);
{
(uint256 action, bytes calldata data) = actions.decodeCall(0);
Expand All @@ -151,4 +147,14 @@ abstract contract SettlerMetaTxn is Permit2PaymentMetaTxn, SettlerBase {
_checkSlippageAndTransfer(slippage);
return true;
}

function executeMetaTxn(
AllowedSlippage calldata slippage,
bytes[] calldata actions,
bytes32, /* zid & affiliate */
address msgSender,
bytes calldata sig
) public virtual metaTx(msgSender, _hashActionsAndSlippage(actions, slippage)) returns (bool) {
return _executeMetaTxn(slippage, actions, sig);
}
}
76 changes: 76 additions & 0 deletions src/chains/Arbitrum/Intent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT
pragma solidity =0.8.25;

import {ArbitrumSettlerMetaTxn} from "./MetaTxn.sol";
import {SettlerIntent} from "../../SettlerIntent.sol";

import {IERC20} from "@forge-std/interfaces/IERC20.sol";
import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol";
import {ISettlerActions} from "../../ISettlerActions.sol";

// Solidity inheritance is stupid
import {SettlerAbstract} from "../../SettlerAbstract.sol";
import {SettlerBase} from "../../SettlerBase.sol";
import {SettlerMetaTxn} from "../../SettlerMetaTxn.sol";
import {SettlerIntent} from "../../SettlerIntent.sol";
import {AbstractContext} from "../../Context.sol";
import {Permit2PaymentAbstract} from "../../core/Permit2PaymentAbstract.sol";
import {Permit2PaymentMetaTxn} from "../../core/Permit2Payment.sol";

/// @custom:security-contact [email protected]
contract ArbitrumSettlerIntent is SettlerIntent, ArbitrumSettlerMetaTxn {
constructor(bytes20 gitCommit) ArbitrumSettlerMetaTxn(gitCommit) {}

// Solidity inheritance is stupid
function executeMetaTxn(
AllowedSlippage calldata slippage,
bytes[] calldata actions,
bytes32, /* zid & affiliate */
address msgSender,
bytes calldata sig
) public override(SettlerIntent, SettlerMetaTxn) returns (bool) {
return super.executeMetaTxn(slippage, actions, bytes32(0), msgSender, sig);
}

function _dispatch(uint256 i, uint256 action, bytes calldata data)
internal
override(ArbitrumSettlerMetaTxn, SettlerBase, SettlerAbstract)
returns (bool)
{
return super._dispatch(i, action, data);
}

function _msgSender() internal view override(SettlerIntent, ArbitrumSettlerMetaTxn) returns (address) {
return super._msgSender();
}

function _witnessTypeSuffix()
internal
pure
override(SettlerIntent, Permit2PaymentMetaTxn)
returns (string memory)
{
return super._witnessTypeSuffix();
}

function _tokenId() internal pure override(SettlerIntent, SettlerMetaTxn, SettlerAbstract) returns (uint256) {
return super._tokenId();
}

function _dispatchVIP(uint256 action, bytes calldata data, bytes calldata sig)
internal
override(ArbitrumSettlerMetaTxn, SettlerMetaTxn)
returns (bool)
{
return super._dispatchVIP(action, data, sig);
}

function _permitToSellAmount(ISignatureTransfer.PermitTransferFrom memory permit)
internal
pure
override(SettlerIntent, Permit2PaymentAbstract, Permit2PaymentMetaTxn)
returns (uint256)
{
return super._permitToSellAmount(permit);
}
}
6 changes: 4 additions & 2 deletions src/chains/Arbitrum/MetaTxn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {AbstractContext} from "../../Context.sol";

/// @custom:security-contact [email protected]
contract ArbitrumSettlerMetaTxn is SettlerMetaTxn, ArbitrumMixin {
constructor(bytes20 gitCommit) SettlerMetaTxn(gitCommit) {}
constructor(bytes20 gitCommit) SettlerBase(gitCommit) {}

function _dispatchVIP(uint256 action, bytes calldata data, bytes calldata sig)
internal
virtual
override
DANGEROUS_freeMemory
returns (bool)
Expand Down Expand Up @@ -53,13 +54,14 @@ contract ArbitrumSettlerMetaTxn is SettlerMetaTxn, ArbitrumMixin {
// Solidity inheritance is stupid
function _dispatch(uint256 i, uint256 action, bytes calldata data)
internal
virtual
override(SettlerAbstract, SettlerBase, ArbitrumMixin)
returns (bool)
{
return super._dispatch(i, action, data);
}

function _msgSender() internal view override(SettlerMetaTxn, AbstractContext) returns (address) {
function _msgSender() internal view virtual override(SettlerMetaTxn, AbstractContext) returns (address) {
return super._msgSender();
}
}
2 changes: 1 addition & 1 deletion src/chains/Arbitrum/TakerSubmitted.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {AbstractContext} from "../../Context.sol";

/// @custom:security-contact [email protected]
contract ArbitrumSettler is Settler, ArbitrumMixin {
constructor(bytes20 gitCommit) Settler(gitCommit) {}
constructor(bytes20 gitCommit) SettlerBase(gitCommit) {}

function _dispatchVIP(uint256 action, bytes calldata data) internal override DANGEROUS_freeMemory returns (bool) {
if (super._dispatchVIP(action, data)) {
Expand Down
Loading
Loading