Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
dc0a166
start deposit
sanbir Mar 17, 2025
075c086
use SingleDirectSingleVaultStateReq
sanbir Mar 19, 2025
a46b313
L2 Missing Event on cloneDeterministic Deployment
sanbir Mar 19, 2025
9b54734
L3 Missing Zero-Address Checks in Constructors
sanbir Mar 19, 2025
c60aaac
fix RPC
sanbir Mar 19, 2025
f2997f2
accept _superformCalldata
sanbir Mar 19, 2025
f770dfd
Merge branch 'ethena-audit-fixes' into superform
sanbir Mar 19, 2025
0cdbb79
check ALLOWED_DEPOSIT_SELECTORS
sanbir Mar 21, 2025
b680e1d
start testing _doWithdraw
sanbir Mar 21, 2025
75ea7c4
make compile
sanbir Mar 24, 2025
5aa1994
deposit works
sanbir Mar 24, 2025
dc14512
P2pYieldProxy__Withdrawn
sanbir Mar 24, 2025
4238849
P2pYieldProxy__Withdrawn
sanbir Mar 24, 2025
d38e07f
increaseAllowance
sanbir Mar 26, 2025
fb4a061
RunTestBase
sanbir Mar 26, 2025
b6596fa
Merge pull request #16 from sanbir/superform
sanbir Mar 27, 2025
80d08cf
run test deposit on base
sanbir Mar 27, 2025
7afd226
run test withdrawal on base
sanbir Mar 28, 2025
1531890
run test deposit on base
sanbir Mar 28, 2025
3108558
run test withdraw 2 on base
sanbir Mar 28, 2025
796ef64
test t6Mh8XAKA-ydhXRTfQmnw
sanbir Mar 28, 2025
c1c871f
test W1wJLZDxXwbebTIhv03cE
sanbir Mar 28, 2025
1fa015e
test W1wJLZDxXwbebTIhv03cE
sanbir Mar 28, 2025
d426119
fix asset
sanbir Apr 2, 2025
5a0c320
add require
sanbir Apr 2, 2025
a9b36cc
OptimismIntegration
sanbir Apr 2, 2025
a8ea946
OptimismIntegration cleanup
sanbir Apr 2, 2025
8f2d7b1
refactor AllowedCalldataChecker
sanbir Apr 2, 2025
61cd2e3
Merge branch 'p2p-org:superform' into superform
sanbir Apr 2, 2025
3ff39c3
add vaultId to log
sanbir Apr 4, 2025
a6ee54c
Merge pull request #17 from sanbir/superform
sanbir Apr 4, 2025
c5076c3
batchClaim
sanbir Apr 7, 2025
14a3090
add ClientBasisPointsOfDeposit
sanbir Apr 7, 2025
a8a38a1
fix tests
sanbir Apr 7, 2025
17d4457
test native batchClaim
sanbir Apr 9, 2025
470e3dc
test_batchclaim_proxy
sanbir Apr 9, 2025
f448d5e
Merge pull request #18 from sanbir/superform
sanbir Apr 9, 2025
6b5bf26
add _depositBatch
sanbir Apr 11, 2025
7f3a353
make compile
sanbir Apr 14, 2025
7261556
start _withdrawBatch
sanbir Apr 14, 2025
e390450
_depositBatch accept non-unique tokens
sanbir Apr 16, 2025
9d075bc
_withdrawBatch for unique assets
sanbir Apr 16, 2025
f10bac5
make compile
sanbir Apr 16, 2025
8dec01c
test OptimismDepositBatch
sanbir Apr 17, 2025
9bd42c5
rename
sanbir Apr 17, 2025
4ca5c76
P2pYieldProxy__Deposited
sanbir Apr 17, 2025
5a3cb7f
P2pYieldProxy__Withdrawn
sanbir Apr 17, 2025
699bf77
RunTestDepositBatchOptimism
sanbir Apr 17, 2025
e299421
start RunTestWithdrawBatchOptimism
sanbir Apr 17, 2025
d114393
OptimismBatchWithNative
sanbir Apr 22, 2025
4aa8014
receive ETH
sanbir Apr 22, 2025
ce2f0bd
batch deposit native
sanbir Apr 22, 2025
b2aa6b3
fix
sanbir Apr 23, 2025
ac73b1b
RunTestDepositBatchOptimism
sanbir Apr 23, 2025
4c24ae7
RunTestWithdrawBatchOptimism
sanbir Apr 23, 2025
a1caf8a
Merge pull request #19 from sanbir/superform
sanbir Apr 23, 2025
6ef9aa0
rm batch
sanbir Apr 23, 2025
87e7878
Merge pull request #20 from sanbir/superform
sanbir Apr 23, 2025
5bafd54
test native
sanbir Apr 24, 2025
da4688e
mv
sanbir Apr 24, 2025
5dbd653
fix
sanbir Apr 24, 2025
d72782d
_doDeposit native
sanbir Apr 25, 2025
19daf5b
test_happyPath_native_Optimism
sanbir Apr 25, 2025
d283cee
test_P2pOperator2Step
sanbir Apr 25, 2025
b115912
test_P2pSuperformProxy__SuperformCalldataTooShort
sanbir Apr 25, 2025
28fb053
test_P2pSuperformProxy__SelectorNotSupported
sanbir Apr 25, 2025
da5c45d
test_P2pSuperformProxy__NativeAmountToDepositAfterFeeLessThanliqReque…
sanbir Apr 25, 2025
579410d
testP2pSuperformProxy__LiqRequestTokenShouldBeEqualToPermitForP2pYiel…
sanbir Apr 25, 2025
81d653f
testP2pSuperformProxy__ShouldNotRetain4626
sanbir Apr 25, 2025
3449796
testP2pSuperformProxy__ReceiverAddressShouldBeP2pSuperformProxy
sanbir Apr 25, 2025
3862606
testP2pSuperformProxy__ReceiverAddressSPShouldBeP2pSuperformProxy
sanbir Apr 25, 2025
a663432
fix
sanbir Apr 25, 2025
2398b13
testAllowedCalldataChecker__NoAllowedCalldata
sanbir Apr 25, 2025
38085bb
testAllowedCalldataCheckerUpgrade
sanbir Apr 25, 2025
f140ad4
test vew funcs
sanbir Apr 25, 2025
17b7ed8
testP2pSuperformProxy_CheckClaim_UnauthorizedAccount
sanbir Apr 25, 2025
7e5ef6f
Merge pull request #21 from sanbir/superform
sanbir Apr 25, 2025
f4adf7c
update README.md
sanbir Apr 29, 2025
677cac6
Merge pull request #22 from sanbir/superform
sanbir Apr 29, 2025
3cb725b
fix H1
sanbir May 9, 2025
429e800
test_happyPath_Morpho_with_deposit_fee
sanbir May 12, 2025
7168afd
fix M1
sanbir May 12, 2025
6933d6b
fix L1
sanbir May 12, 2025
e1451bd
fix L2
sanbir May 12, 2025
ef3e18f
fix L3
sanbir May 12, 2025
e356e45
fix L4
sanbir May 12, 2025
ddb0a5e
fix L5
sanbir May 12, 2025
5473e70
pragma solidity 0.8.27;
sanbir May 12, 2025
b7b2a4f
Merge pull request #24 from sanbir/superform-audit-fixes
sanbir May 12, 2025
57ca855
calculateAccruedRewards
sanbir Oct 15, 2025
33f9584
Merge branch 'p2p-org:superform' into superform
sanbir Oct 15, 2025
c291923
Merge pull request #29 from sanbir/superform
sanbir Oct 15, 2025
9467d44
start superform-test
sanbir Oct 29, 2025
777f2e7
rm Permit2
sanbir Oct 29, 2025
3dacd71
test_calculateAccruedRewards_AfterDeposit
sanbir Oct 29, 2025
7e1eb9e
add withdrawAccruedRewards
sanbir Oct 30, 2025
1ec1817
fix
sanbir Oct 30, 2025
d8a1ab0
clean up
sanbir Oct 30, 2025
d2c9f01
Merge pull request #36 from p2p-org/superform-test
sanbir Oct 30, 2025
06b2079
rm Permit2
sanbir Oct 30, 2025
f54c1db
add docs
sanbir Oct 30, 2025
bfe6f0d
add test_doubleFeeCollectionBug_SuperformFlow
sanbir Nov 4, 2025
7023410
test_PartialWithdraw added to simulate overcharged fees
Iamshashvat Nov 5, 2025
6be8b51
fix: fee calculation
Iamshashvat Nov 5, 2025
e4f2136
Merge pull request #38 from Iamshashvat/superform
sanbir Nov 5, 2025
64347f6
refactor: centralize P2P fee logic into calculateP2pFeeAmount()
Iamshashvat Nov 5, 2025
63021bf
Merge branch 'p2p-org:superform' into superform
Iamshashvat Nov 5, 2025
5da7e48
Merge pull request #39 from Iamshashvat/superform
sanbir Nov 5, 2025
a7710ba
cross-chain deploy
sanbir Nov 11, 2025
04e64b1
add
sanbir Nov 11, 2025
dc1648f
Squashed PR commits
sanbir Nov 19, 2025
de143c2
add docs
sanbir Oct 30, 2025
998c9c8
Merge pull request #46 from sanbir/superform
sanbir Nov 19, 2025
70e4646
Merge remote-tracking branch 'origin/superform' into superform
sanbir Nov 20, 2025
3a023da
fix calculateMinAmountToApproveForDeposit
sanbir Dec 1, 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
117 changes: 61 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## p2p-yield-proxy

Contracts for depositing and withdrawing ERC-20 tokens from yield protocols.
The current implementation is only compatible with [Ethena](https://ethena.fi/) protocol.
The current implementation is only compatible with [Superform](https://www.superform.xyz/) protocol.

## Running tests

Expand All @@ -18,25 +18,35 @@ forge test
forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --chain $CHAIN_ID --json --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvvv
```

`Deploy.s.sol` deterministically deploys the shared Superform tooling using CREATE2 so that every network produces the same contract addresses.
Set the following environment variables (override the defaults only when a network requires different endpoints):

- `PRIVATE_KEY` – broadcaster key (also becomes the default `P2P_SIGNER`)
- `P2P_SIGNER` – optional override for the signer account
- `P2P_OPERATOR` – optional override for the operator account (defaults to `P2P_SIGNER`)
- `SUPERFORM_ROUTER`
- `SUPER_POSITIONS`
- `P2P_TREASURY`
- `REWARDS_DISTRIBUTOR`

This script will:

- deploy and verify on Etherscan the **P2pEthenaProxyFactory** and **P2pEthenaProxy** contracts
- set the **P2pTreasury** address permanently in the P2pEthenaProxyFactory
- set the rules for Ethena specific deposit and withdrawal functions
- deploy and verify on Etherscan the **P2pSuperformProxyFactory**, its reference **P2pSuperformProxy**, the **AllowedCalldataChecker** implementation, proxy, and **ProxyAdmin**
- ensure all CREATE2 salts are reused so the deployed addresses stay identical on Base, Optimism, Mainnet, and any additional chains

## Basic use case

![Basic use case diagram](image-1.png)

#### Ethena Deposit flow
#### Superform Deposit flow

Look at [function _doDeposit()](test/MainnetIntegration.sol#L1000) for a reference implementation of the flow.
Look at [function _doDeposit()](test/OptimismUSDT.t.sol#L430) for a reference implementation of the flow.

1. Website User (called Client in contracts) calls Backend with its (User's) Ethereum address and some Merchant info.

2. Backend uses Merchant info to determine the P2P fee (expressed as client basis points in the contracts).

3. Backend calls P2pEthenaProxyFactory's `getHashForP2pSigner` function to get the hash for the P2pSigner.
3. Backend calls P2pSuperformProxyFactory's `getHashForP2pSigner` function to get the hash for the P2pSigner.

```solidity
/// @dev Gets the hash for the P2pSigner
Expand All @@ -46,101 +56,96 @@ Look at [function _doDeposit()](test/MainnetIntegration.sol#L1000) for a referen
/// @return The hash for the P2pSigner
function getHashForP2pSigner(
address _client,
uint96 _clientBasisPoints,
uint48 _clientBasisPoints,
uint256 _p2pSignerSigDeadline
) external view returns (bytes32);
```

4. Backend signs the hash with the P2pSigner's private key using `eth_sign`. Signing is necessary to authenticate the client basis points in the contracts.

5. Backend returns JSON to the User with (client address, client basis points, signature deadline, and the signature).
5. Backend calls Superform API to generate Superform deposit calldata.

6. Backend returns JSON to the User with (client address, client basis points, signature deadline, and the signature).

6. Client-side JS code prepares all the necessary data for the Morpho deposit function. Since the deposited tokens will first go from the client to the client's P2pEthenaProxy instance and then from the P2pEthenaProxy instance into the Ethena protocol, both of these transfers are approved by the client via Permit2. The client's P2pEthenaProxy instance address is fetched from the P2pEthenaProxyFactory contract's `predictP2pEthenaProxyAddress` function:
7. Client-side JS code prepares all the necessary data for the Morpho deposit function. The client's P2pSuperformProxy instance address is fetched from the P2pSuperformProxyFactory contract's `predictP2pYieldProxyAddress` function:

```solidity
/// @dev Computes the address of a P2pEthenaProxy created by `_createP2pEthenaProxy` function
/// @dev P2pEthenaProxy instances are guaranteed to have the same address if _feeDistributorInstance is the same
/// @dev Computes the address of a P2pYieldProxy created by `_getOrCreateP2pYieldProxy` function
/// @dev P2pYieldProxy instances are guaranteed to have the same address if _feeDistributorInstance is the same
/// @param _client The address of client
/// @return address The address of the P2pEthenaProxy instance
function predictP2pEthenaProxyAddress(
/// @param _clientBasisPointsOfDeposit The client basis points (share) of deposit
/// @param _clientBasisPointsOfProfit The client basis points (share) of profit
/// @return address The address of the P2pYieldProxy instance
function predictP2pYieldProxyAddress(
address _client,
uint96 _clientBasisPoints
uint48 _clientBasisPointsOfDeposit,
uint48 _clientBasisPointsOfProfit
) external view returns (address);
```

7. Client-side JS code checks if User has already approved the required amount of the deposited token for Permit2. If not, it prompts the User to call the `approve` function of the deposited token contract with the uint256 MAX value and Permit2 contract as the spender.
8. Client-side JS code checks if the user has already approved the required amount of the deposited token for the P2pSuperformProxy instance. If not, it prompts the user to call the ERC20 `approve` function with an allowance that covers the intended deposit amount.

8. Client-side JS code prompts the User to do `eth_signTypedData_v4` to sign `PermitSingle` from the User's wallet into the P2pEthenaProxy instance

9. Client-side JS code prompts the User to call the `deposit` function of the P2pEthenaProxyFactory contract:
9. Client-side JS code prompts the user to call the `deposit` function of the P2pSuperformProxyFactory contract:

```solidity
/// @dev Deposits the yield protocol
/// @param _permitSingleForP2pEthenaProxy The permit single for P2pEthenaProxy
/// @param _permit2SignatureForP2pEthenaProxy The permit2 signature for P2pEthenaProxy
/// @param _clientBasisPoints The client basis points
/// @dev Initiates a deposit through a client specific P2pYieldProxy instance
/// @param _yieldProtocolCalldata Yield protocol calldata
/// @param _clientBasisPointsOfDeposit The client basis points (share) of deposit
/// @param _clientBasisPointsOfProfit The client basis points (share) of profit
/// @param _p2pSignerSigDeadline The P2pSigner signature deadline
/// @param _p2pSignerSignature The P2pSigner signature
/// @return P2pEthenaProxyAddress The client's P2pEthenaProxy instance address
/// @return p2pYieldProxyAddress The client's P2pYieldProxy instance address
function deposit(
IAllowanceTransfer.PermitSingle memory _permitSingleForP2pEthenaProxy,
bytes calldata _permit2SignatureForP2pEthenaProxy,

uint96 _clientBasisPoints,
bytes calldata _yieldProtocolCalldata,
uint48 _clientBasisPointsOfDeposit,
uint48 _clientBasisPointsOfProfit,
uint256 _p2pSignerSigDeadline,
bytes calldata _p2pSignerSignature
)
external
returns (address P2pEthenaProxyAddress);
payable
returns (address p2pYieldProxyAddress);
```

#### Ethena Withdrawal flow
#### Superform Withdrawal flow

Look at [function _doWithdraw()](test/MainnetIntegration.sol#L1024) for a reference implementation of the flow.
Look at [function _doWithdraw()](test/OptimismUSDT.t.sol#L486) for a reference implementation of the flow.

1. Client-side JS code prepares all the necessary data for the Ethena `cooldownShares` function.
1. Website User calls P2P.org's backend for Superform withdrawal calldata.

2. Client-side JS code prompts the User to call the `cooldownShares` function of the client's instance of the P2pEthenaProxy contract:
2. P2P.org's backend calls Superform API for Superform withdrawal calldata.

```solidity
/// @notice redeem shares into assets and starts a cooldown to claim the converted underlying asset
/// @param _shares shares to redeem
function cooldownShares(uint256 _shares) external returns (uint256 assets);
```
3. P2P.org's backend returns Superform withdrawal calldata to the User.

3. Wait for the [cooldownDuration](https://etherscan.io/address/0x9d39a5de30e57443bff2a8307a4256c8797a3497#readContract#F9). (Currently, 7 days).
4. Client-side JS code prepares all the necessary data for the Superform `singleDirectSingleVaultWithdraw` function.

2. Client-side JS code prompts the User to call the `withdrawAfterCooldown` function of the client's instance of the P2pEthenaProxy contract:
5. Client-side JS code prompts the User to call the `withdraw` function of the client's instance of the P2pSuperformProxy contract:

```solidity
/// @notice withdraw assets after cooldown has elapsed
function withdrawAfterCooldown() external;
/// @notice Withdraw assets from Superform protocol
/// @param _superformCalldata calldata for withdraw function of Superform protocol
function withdraw(
bytes calldata _superformCalldata
) external;
```

The P2pEthenaProxy contract will redeem the tokens from Ethena and send them to User. The amount on top of the deposited amount is split between the User and the P2pTreasury according to the client basis points.
The P2pSuperformProxy contract will redeem the tokens from Superform and send them to User. The amount on top of the deposited amount is split between the User and the P2pTreasury according to the client basis points.


## Calling any function on any contracts via P2pEthenaProxy
## Calling any function on any contracts via P2pSuperformProxy

It's possible for the User to call any function on any contracts via P2pEthenaProxy. This can be useful if it appears that functions of yield protocols beyond simple deposit and withdrawal are needed. Also, it can be useful for claiming any airdrops unknown in advance.
It's possible for the User to call any function on any contracts via P2pSuperformProxy. This can be useful if it appears that functions of yield protocols beyond simple deposit and withdrawal are needed. Also, it can be useful for claiming any airdrops unknown in advance.

Before the User can use this feature, the P2P operator needs to set the rules for the function call via the `setCalldataRules` function of the P2pEthenaProxyFactory contract:
Before the User can use this feature, the P2P operator needs to deploy a new contract implementing the `IAllowedCalldataChecker` interface and then the function `upgrade` function on ProxyAdmin:

```solidity
/// @dev Sets the calldata rules
/// @param _contract The contract address
/// @param _selector The selector
/// @param _rules The rules
function setCalldataRules(
address _contract,
bytes4 _selector,
P2pStructs.Rule[] calldata _rules
) external;
MockAllowedCalldataChecker newImplementation = new MockAllowedCalldataChecker();
admin.upgrade(ITransparentUpgradeableProxy(address(tup)), address(newImplementation));
```

The rules should be as strict as possible to prevent any undesired function calls.

Once the rules are set, the User can call the permitted function on the permitted contract with the permitted calldata via P2pEthenaProxy's `callAnyFunction` function:
Once the rules are set, the User can call the permitted function on the permitted contract with the permitted calldata via P2pSuperformProxy's `callAnyFunction` function:

```solidity
/// @notice Calls an arbitrary allowed function
Expand Down
4 changes: 3 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
[profile.default]
ffi = true
fs_permissions = [{ access = "read-write", path = "./" }]
src = "src"
out = "out"
libs = ["lib"]
Expand All @@ -9,4 +11,4 @@ via_ir = true
optimizer = true
optimizer-runs = 2000

rpc_endpoints = { mainnet = "https://rpc.ankr.com/eth", sepolia = "https://rpc.ankr.com/eth_sepolia", base = "https://mainnet.base.org" }
rpc_endpoints = { mainnet = "https://eth.drpc.org", optimism = "https://mainnet.optimism.io", base = "https://mainnet.base.org" }
Binary file modified image-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lib/forge-std
Loading