Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a5f7ead
prevent stack to deep
ezynda3 Apr 28, 2025
5bdbb68
add demo script
ezynda3 May 8, 2025
fb94bcc
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 May 14, 2025
6428887
refactor
ezynda3 May 16, 2025
5e1234e
TX is successful now
ezynda3 May 19, 2025
79a6357
Cleanup
ezynda3 May 19, 2025
08c23e3
Fix cowswap call
ezynda3 May 20, 2025
b649bf9
Fix patch calls
ezynda3 May 20, 2025
44fdac0
Merge branch 'main' into patcher
ezynda3 May 21, 2025
5a2625d
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 May 23, 2025
9e6b225
Merge branch 'patcher' of github.com:lifinance/contracts into patcher
ezynda3 May 23, 2025
7aa9fd5
Simplify
ezynda3 May 23, 2025
3d40310
fix cow swap submission
ezynda3 May 23, 2025
cc451c7
use relay
ezynda3 May 23, 2025
aae8914
use correct calc
ezynda3 May 23, 2025
fa932d1
fixes
ezynda3 May 23, 2025
a71e1ab
use correct cowshed calls
ezynda3 May 23, 2025
df48f1c
use real sig
ezynda3 May 23, 2025
b709aef
updates
ezynda3 May 23, 2025
043c9a1
calc offset
ezynda3 May 23, 2025
452fb41
use viem
ezynda3 May 26, 2025
0ad1c06
add tests
ezynda3 May 26, 2025
379f363
update tests
ezynda3 May 26, 2025
e3912cc
update tests
ezynda3 May 26, 2025
801e181
add approval
ezynda3 May 26, 2025
3c12423
fix
ezynda3 May 26, 2025
f9fe5ba
refactor
ezynda3 May 27, 2025
27f2fb5
more refactor
ezynda3 May 27, 2025
d2a422f
cleanup resources
ezynda3 May 27, 2025
c7c515c
begin adding dest call
ezynda3 May 27, 2025
2cd19ad
use separate scripts
ezynda3 May 27, 2025
f137d91
add patch calls to dest call
ezynda3 May 27, 2025
868d761
use offset finding utils
ezynda3 May 28, 2025
33729fd
refactor
ezynda3 May 28, 2025
20a85bf
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 Jun 3, 2025
b8752a0
add deposit functions to patcher
ezynda3 Jun 3, 2025
640dc7d
allow depositing to patcher
ezynda3 Jun 3, 2025
4cb8002
fix route
ezynda3 Jun 3, 2025
6bacd74
tweaks
ezynda3 Jun 3, 2025
5b4b57c
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 Jun 4, 2025
4dd901b
fix scripts
ezynda3 Jun 4, 2025
2278238
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 Jun 4, 2025
79d002c
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 Jun 5, 2025
c9d4bfa
remove
ezynda3 Jun 5, 2025
f364cec
clarifying comments
ezynda3 Jun 5, 2025
d66fa4e
add documentation
ezynda3 Jun 5, 2025
9b4a029
fix payable issue
ezynda3 Jun 5, 2025
55f5626
Merge branch 'main' of github.com:lifinance/contracts into patcher
ezynda3 Jun 9, 2025
77d0b0f
fixes
ezynda3 Jun 16, 2025
c102051
more fixes
ezynda3 Jun 16, 2025
5af3d3e
more fixes
ezynda3 Jun 16, 2025
6790e43
more fixes
ezynda3 Jun 16, 2025
03ea5a0
remove commented code
ezynda3 Jun 16, 2025
5d4d29a
fix
ezynda3 Jun 25, 2025
6f47673
added _createPatchedData
mirooon Jul 4, 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
307 changes: 268 additions & 39 deletions bun.lock

Large diffs are not rendered by default.

54 changes: 50 additions & 4 deletions deployments/_deployments_log_file.json
Original file line number Diff line number Diff line change
Expand Up @@ -30339,9 +30339,9 @@
],
"1.1.0": [
{
"ADDRESS": "0x08BfAc22A3B41637edB8A7920754fDb30B18f740",
"ADDRESS": "0xF336cc028Fc5328472f96e377d32Fd32F8eE1750",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2024-12-31 12:23:04",
"TIMESTAMP": "2025-05-27 13:50:22",
"CONSTRUCTOR_ARGS": "0x000000000000000000000000e35e9842fceaca96570b734083f4a58e8f7c5f2a00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
"SALT": "",
"VERIFIED": "true"
Expand Down Expand Up @@ -32870,9 +32870,9 @@
"staging": {
"1.0.0": [
{
"ADDRESS": "0x3cf7dE0e31e13C93c8Aada774ADF1C7eD58157f5",
"ADDRESS": "0x681a3409c35F12224c436D50Ce14F25f954B6Ea2",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2024-11-07 14:15:11",
"TIMESTAMP": "2025-05-23 17:41:51",
"CONSTRUCTOR_ARGS": "0x000000000000000000000000a5f565650890fba1824ee0f21ebbbf660a179934000000000000000000000000f70da97812cb96acdf810712aa562db8dfa3dbef",
"SALT": "",
"VERIFIED": "true"
Expand Down Expand Up @@ -33830,5 +33830,51 @@
]
}
}
},
"WhitelistManagerFacet": {
"arbitrum": {
"staging": {
"1.0.3": [
{
"ADDRESS": "0x603f0c31B37E5ca3eA75D5730CCfaBCFF6D17aa3",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-05-16 14:32:49",
"CONSTRUCTOR_ARGS": "0x",
"SALT": "",
"VERIFIED": "true"
}
]
}
}
},
"Patcher": {
"arbitrum": {
"staging": {
"1.0.0": [
{
"ADDRESS": "0x18069208cA7c2D55aa0073E047dD45587B26F6D4",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-06-04 12:55:52",
"CONSTRUCTOR_ARGS": "0x",
"SALT": "",
"VERIFIED": "true"
}
]
}
},
"base": {
"staging": {
"1.0.0": [
{
"ADDRESS": "0x18069208cA7c2D55aa0073E047dD45587B26F6D4",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-06-03 16:39:25",
"CONSTRUCTOR_ARGS": "0x",
"SALT": "",
"VERIFIED": "true"
}
]
}
}
}
}
3 changes: 2 additions & 1 deletion deployments/arbitrum.diamond.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@
"ReceiverChainflip": "",
"ReceiverStargateV2": "",
"RelayerCelerIM": "0xa1Ed8783AC96385482092b82eb952153998e9b70",
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70"
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70",
"Patcher": "0x3971A968c03cd9640239C937F8d30D024840E691"
}
}
}
10 changes: 6 additions & 4 deletions deployments/arbitrum.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70",
"EmergencyPauseFacet": "0x17Bb203F42d8e404ac7E8dB6ff972B7E8473850b",
"Permit2Proxy": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5",
"AcrossFacetV3": "0x08BfAc22A3B41637edB8A7920754fDb30B18f740",
"AcrossFacetV3": "0xF336cc028Fc5328472f96e377d32Fd32F8eE1750",
"ReceiverAcrossV3": "0xe4F3DEF14D61e47c696374453CD64d438FD277F8",
"AcrossFacetPackedV3": "0x21767081Ff52CE5563A29f27149D01C7127775A2",
"RelayFacet": "0x3cf7dE0e31e13C93c8Aada774ADF1C7eD58157f5",
"RelayFacet": "0x681a3409c35F12224c436D50Ce14F25f954B6Ea2",
"GlacisFacet": "0xF82830B952Bc60b93206FA22f1cD4770cedb2840",
"GasZipFacet": "0x37f3F3E9d909fB1163448C511193b8481e541C62",
"ChainflipFacet": "0xa884c21873A671bD010567cf97c937b153F842Cc"
}
"ChainflipFacet": "0xa884c21873A671bD010567cf97c937b153F842Cc",
"Patcher": "0x18069208cA7c2D55aa0073E047dD45587B26F6D4",
"WhitelistManagerFacet": "0x603f0c31B37E5ca3eA75D5730CCfaBCFF6D17aa3"
}
3 changes: 2 additions & 1 deletion deployments/base.staging.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"StandardizedCallFacet": "0x637Ac9AddC9C38b3F52878E11620a9060DC71d8B"
"StandardizedCallFacet": "0x637Ac9AddC9C38b3F52878E11620a9060DC71d8B",
"Patcher": "0x18069208cA7c2D55aa0073E047dD45587B26F6D4"
}
163 changes: 163 additions & 0 deletions docs/Patcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Patcher

## Description

The Patcher is a utility contract that enables dynamic calldata patching before execution. It allows you to retrieve values dynamically from external contracts and use them to patch specific positions in calldata before executing the final call. This is particularly useful for scenarios where exact amounts are only known at execution time, such as token balances after deposits or bridge transfers.

## How it works

The Patcher works by:
1. Retrieving dynamic values from external contracts via static calls
2. Patching these values into predetermined positions in calldata
3. Executing the final call with the patched data

```mermaid
graph LR;
A[User] --> B[Patcher Contract]
B --> C[Value Source Contract]
C --> B
B --> D[Final Target Contract]
B --> E[Patch Calldata]
E --> D
```

## Public Methods

### Single Value Patching

- `function executeWithDynamicPatches(address valueSource, bytes calldata valueGetter, address finalTarget, uint256 value, bytes calldata data, uint256[] calldata offsets, bool delegateCall)`
- Retrieves a single dynamic value and patches it into multiple positions in the calldata before execution

### Token Deposit with Single Value Patching

- `function depositAndExecuteWithDynamicPatches(address tokenAddress, address valueSource, bytes calldata valueGetter, address finalTarget, uint256 value, bytes calldata data, uint256[] calldata offsets, bool delegateCall)`
- Transfers the caller's entire token balance to the Patcher, approves the final target, then executes with dynamic patching

### Multiple Value Patching

- `function executeWithMultiplePatches(address[] calldata valueSources, bytes[] calldata valueGetters, address finalTarget, uint256 value, bytes calldata data, uint256[][] calldata offsetGroups, bool delegateCall)`
- Retrieves multiple dynamic values from different sources and patches them into different positions in the calldata

### Token Deposit with Multiple Value Patching

- `function depositAndExecuteWithMultiplePatches(address tokenAddress, address[] calldata valueSources, bytes[] calldata valueGetters, address finalTarget, uint256 value, bytes calldata data, uint256[][] calldata offsetGroups, bool delegateCall)`
- Transfers the caller's entire token balance to the Patcher, approves the final target, then executes with multiple dynamic patches

## Parameters

### Common Parameters

- `valueSource` / `valueSources`: The contract(s) to query for dynamic values
- `valueGetter` / `valueGetters`: The calldata to use for retrieving the dynamic value(s) (e.g., `balanceOf(address)` call)
- `finalTarget`: The contract to call with the patched data
- `value`: The ETH value to send with the final call
- `data`: The original calldata to patch and execute
- `offsets` / `offsetGroups`: Byte offset(s) in the calldata where the dynamic value(s) should be written
- `delegateCall`: Whether to use delegatecall instead of a regular call for the final execution
- `tokenAddress`: The ERC20 token to transfer from the caller (for deposit methods)

### Offset Calculation

Offsets specify the exact byte position in the calldata where a 32-byte value should be written. These are typically calculated by:
1. Encoding the target function call with placeholder values
2. Finding the byte position of the placeholder in the encoded data
3. Using that position as the offset

## Use Cases

### Cross-Chain Bridge with Destination Swap

The Patcher is particularly useful for cross-chain scenarios where the exact amount received from a bridge is unknown until execution:

1. **Bridge tokens** from source chain to destination chain
2. **Receive variable amount** due to fees, slippage, or exchange rates
3. **Use Patcher** to query the actual received balance
4. **Patch the balance** into a swap call to use the exact amount received
5. **Execute the swap** with the correct amount

### Dynamic Balance Swaps

When you need to swap the entire balance of a token but don't know the exact amount:

1. **Deposit tokens** to the Patcher contract
2. **Query the balance** dynamically
3. **Patch the balance** into a DEX swap call
4. **Execute the swap** using the entire deposited amount

### Oracle-Based Transactions

For transactions that depend on real-time data:

1. **Query price oracles** or other data sources
2. **Patch the values** into transaction parameters
3. **Execute** with up-to-date information

## Error Handling

The Patcher includes several error types for different failure scenarios:

- `FailedToGetDynamicValue()`: Thrown when the static call to retrieve a dynamic value fails
- `MismatchedArrayLengths()`: Thrown when input arrays have different lengths in multiple patch methods
- `InvalidPatchOffset()`: Thrown when a patch offset would write beyond the calldata bounds

## Security Considerations

- The Patcher uses `staticcall` to retrieve dynamic values, ensuring no state changes during value retrieval
- Offset validation prevents writing beyond calldata boundaries
- The contract is designed to be used with delegate calls for integration into larger systems
- Token approvals are given to the final target contract, not stored permanently

## Integration Patterns

### With Bridge Receivers

The Patcher is commonly used in bridge receiver contracts to handle variable bridge amounts:

```solidity
// Pseudo-code example
function handleBridgeMessage(bytes calldata message) external {
// Decode swap parameters from message
SwapData memory swapData = abi.decode(message, (SwapData));

// Use Patcher to deposit received tokens and execute swap with exact balance
patcher.depositAndExecuteWithDynamicPatches(
bridgedToken,
bridgedToken, // value source
abi.encodeCall(IERC20.balanceOf, (address(patcher))), // value getter
dexAggregator, // final target
0, // no ETH value
swapCalldata, // calldata with placeholder amount
[amountOffset], // where to patch the amount
false // regular call
);
}
```

### With DEX Aggregators

For swapping entire token balances through DEX aggregators:

```solidity
// Pseudo-code example
function swapEntireBalance(address token, bytes calldata swapCalldata, uint256 amountOffset) external {
patcher.depositAndExecuteWithDynamicPatches(
token,
token, // query token balance
abi.encodeCall(IERC20.balanceOf, (address(patcher))),
dexAggregator,
0,
swapCalldata,
[amountOffset],
false
);
}
```

## Best Practices

1. **Calculate offsets carefully**: Ensure offsets point to the correct parameter positions in the calldata
2. **Use appropriate value getters**: Choose the right function to call for retrieving dynamic values
3. **Handle failures gracefully**: The Patcher will revert if value retrieval fails
4. **Consider gas costs**: Multiple patches and complex calls increase gas usage
5. **Test thoroughly**: Dynamic patching can be complex - test with various scenarios
6. **Validate inputs**: Ensure array lengths match for multiple patch operations
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
- [Executor](./Executor.md)
- [FeeCollector](./FeeCollector.md)
- [LiFuelFeeCollector](./LiFuelFeeCollector.md)
- [Patcher](./Patcher.md)
- [Receiver](./Receiver.md)
- [ReceiverStargateV2](./ReceiverStargateV2.md)
- [ReceiverChainflip](./ReceiverChainflip.md)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
},
"dependencies": {
"@arbitrum/sdk": "^3.0.0",
"@cowprotocol/cow-sdk": "^5.10.3",
"@hop-protocol/sdk": "0.0.1-beta.310",
"@layerzerolabs/lz-v2-utilities": "^2.3.21",
"@ledgerhq/hw-app-eth": "^6.43.0",
Expand Down
Loading
Loading