Skip to content

Commit

Permalink
Merge pull request #22 from smart-transaction/xiangan/examples
Browse files Browse the repository at this point in the history
[wip] Additions to example PR
  • Loading branch information
xBalbinus authored Oct 11, 2023
2 parents 86da63c + 9f9614b commit e199752
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 17 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Smart Transactions Core Contracts

Smart transactions core contracts are a set of smart contracts that allow to create and manage transactions with temporal properties. Smart transactions are transactions that are executed at MEV time and queued up beforehand with a certain delay. For example, a smart transaction can be created to transfer a certain amount of tokens to a specific address when a certain date is reached, or to create some continuous scheduled transfer of funds to a specific address.

## Usage

To begin development, run the following commands:

```bash
forge install
forge build
# Run test suite
forge test
```

## Call Flow Example

One such example of the usage of smart transactions is located inside the Worked Example test. This is a test that simulates the creation of a smart transaction that will swap a certain amount of tokens to a specific address when a certain date is reached. The test is divided into 3 parts:
- Deployment of the contracts
- Creation of the smart transaction from the user
- Execution of the smart transaction (and thereby the swapping of funds) by the miner

### Deployment of the contracts

The first part of the test is the deployment of the contracts. The contracts that are deployed are the following:
- ERC20 token contract
- Smart transaction factory contract
- Smart transaction executor contract

10 Token A are minted to the user and 20 Token B are minted to the executor. The executor contract is the contract that will execute the smart transaction when the time comes. The user will create the smart transaction and will receive the funds when the swap is fulfilled by the executor ('filler').

### Creation of the smart transaction from the user

The user queues up a call to the `approve` function to approve some arbitrary exchange to take 10 Token A at a fixed rate for 20 Token B in return at the time of fulfillment. The user then queues a call to the function to transfer 10 Token A to the exchange. The user queues the call by pushing the encoded calls to the `LaminatedProxy`, a mempool implementation for fulfillers of the swap to pull swaps from.

### Execution of the smart transaction by the miner

At a high level, the executor orchestrates a sequence of contract interactions and ensures their validity by calling the `verify` function at the end. Here, in order to fulfill the swap, the solver specifically:
- Preparing Calls and Expected Response States
- preClean Call
- Setting up Approval and Swapping Partner
- Performing the swap (Taking 10 Token A, giving 20 Token B)
- Checking balances (Ensuring that the swap was successful)
- Cleanup Call
- Verification of correct call execution
2 changes: 2 additions & 0 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.6.2 <0.9.0;
import "forge-std/Script.sol";
import "../src/lamination/Laminator.sol";
import "../src/timetravel/CallBreaker.sol";
import "./CleanupUtility.sol";

contract DeployScript is Script {
function run() external {
Expand All @@ -12,6 +13,7 @@ contract DeployScript is Script {

Laminator laminator = new Laminator();
CallBreaker callbreaker = new CallBreaker();
CleanupUtility cleanupContract = new CleanupUtility();

vm.stopBroadcast();
}
Expand Down
27 changes: 14 additions & 13 deletions src/timetravel/CallBreaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ contract CallBreaker is CallBreakerStorage {
ensureAllPairsAreBalanced();

cleanUpStorage();

// Transfer remaining ETH balance to the block builder
address payable blockBuilder = payable(block.coinbase);
emit VerifyStxn();
Expand All @@ -113,7 +113,8 @@ contract CallBreaker is CallBreakerStorage {
revert OutOfEther();
}

(bool success, bytes memory returnvalue) = callObj.addr.call{gas: callObj.gas, value: callObj.amount}(callObj.callvalue);
(bool success, bytes memory returnvalue) =
callObj.addr.call{gas: callObj.gas, value: callObj.amount}(callObj.callvalue);
if (!success) {
revert CallFailed();
}
Expand All @@ -122,15 +123,6 @@ contract CallBreaker is CallBreakerStorage {
decrementCallBalance(pairID);
}

/// @dev Ensures all call-return pairs have balanced counts.
function ensureAllPairsAreBalanced() internal view {
for (uint256 i = 0; i < callbalanceKeyList.length; i++) {
if (callbalanceStore[callbalanceKeyList[i]].balance != 0) {
revert TimeImbalance();
}
}
}

/// @dev Cleans up storage by resetting returnStore and callbalanceKeyList
function cleanUpStorage() internal {
delete returnStore;
Expand All @@ -143,7 +135,7 @@ contract CallBreaker is CallBreakerStorage {
returnStore.pop();
return lastReturn;
}

/// @dev Helper function to increment the balance of a call-return pair in the storage.
/// @param pairID The unique identifier for a call-return pair.
function incrementCallBalance(bytes32 pairID) internal {
Expand All @@ -155,7 +147,7 @@ contract CallBreaker is CallBreakerStorage {
callbalanceStore[pairID].balance++;
}
}

/// @dev Helper function to decrement the balance of a call-return pair in the storage.
/// @param pairID The unique identifier for a call-return pair.
///
Expand All @@ -168,4 +160,13 @@ contract CallBreaker is CallBreakerStorage {
callbalanceStore[pairID].balance--;
}
}

/// @dev Ensures all call-return pairs have balanced counts.
function ensureAllPairsAreBalanced() internal view {
for (uint256 i = 0; i < callbalanceKeyList.length; i++) {
if (callbalanceStore[callbalanceKeyList[i]].balance != 0) {
revert TimeImbalance();
}
}
}
}
8 changes: 4 additions & 4 deletions test/examples/TemporalStates.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import "../../src/timetravel/CallBreaker.sol";
contract TemporalHoneypot {
address private _callbreakerAddress;
IERC20 atoken;

error NotEmpty();

bool withdrawalScheduled;
address withdrawer;

error NotEmpty();

event Transfer(address indexed from, address indexed to, uint256 value);

constructor(address callbreakerLocation, address _atoken) {
Expand Down Expand Up @@ -82,11 +81,12 @@ contract MEVTimeOracle {
constructor() {}

event LogBytesReceived(bytes data);
event LogFeeReceived(uint256 fee);

// Returns 'some arbitrary amount' to withdraw
function returnArbitraryData(uint256 fee, bytes memory seed) public returns (bytes memory) {
// Oracle takes fee, returns some arbitrary data

emit LogFeeReceived(fee);
// Arbitrary data processing happens here
emit LogBytesReceived(seed);
return seed;
Expand Down

0 comments on commit e199752

Please sign in to comment.