--- tags: Projects --- # Swap :::info **Released:** April 18th, 12:00pm EST **Due:** May 1st, 11:59pm EST ::: ## Introduction :::info **What happened to Auction?** This project was originally titled Auction and was slated to be a cross-chain auction protocol. We've opted instead for a cross-chain swap protocol. ::: You've now worked across several different aspects of blockchains. First, you implemented a blockchain storage solution in Chain. Then, you added a consensus mechanism in Coin, allowing us to run a fully (mostly?) functioning cryptocurrency. Lastly, you implemented a scaling solution in Lightning. Now, it's time to dip your toes into smart contracts and cross-chain commerce. If you haven't already completed the [**Solidity lab**](https://hackmd.io/@cs1951L/lab2-solidity), do that first. It covers Hardhat installation, which is required for this project. #### Important Concepts Before we begin, it's worth reviewing some of the foundations for this project. * **Escrowing:** In standard finance, escrow accounts are owned by third parties to hold assets until some condition is fulfilled. In blockchain finance, those third parties are smart contracts. Putting those together, escrowing an asset on a blockchain means transferring custody to a smart contract until some condition is fulfilled. * **Cross-Chain Commerce:** Cross-chain commerce allows us to make financial exchanges across multiple blockchains that cannot communicate with each other. For example, we could have cross-chain commerce by using the incompatible Bitcoin and Ethereum chains (although any such implementation wouldn't be feature-rich, as Bitcoin lacks smart contracts) * **Hashlocks:** hashlocks are the backbone of many smart contracts. I'm free to publish $H(S)$ as far and wide as I like, since nobody will be able to reverse-engineer $S$. The fulfillment condition for releasing an escrowed asset is providing $S$, which the contract can quickly verify hashes to $H(S)$. ### Hedging Against Sore Lose Attacks Here's the [**original paper**](https://arxiv.org/pdf/2105.06322.pdf) describing the protocol we'll cover below. You should read up until the start of Section 6, which is a total of just under 4 pages. If you do not, you likely will not understand the purpose of this protocol: limiting the impact of sore loser attacks. :::info **Sore loser attack:** When one party decides to halt participation in cross-chain commerce partway through a protocol, leaving the other parties’ assets locked up for a long duration (Xue, 2021). ::: ### The Protocol In this example, Alice sets up a cross-chain swap with Bob. She owns Apricots and he owns Bananas. She wants to trade some of her Apricots for some of his Bananas. Some of the communication between Alice and Bob must happen off chain, such as Alice telling Bob the hashLock for the swaps. For simplicity's sake, the protocol only depicts on-chain communication, which is the part that you will be implementing. #### Notation **P_a:** Alice's premium amount **P_b:** Bob's premium amount **A_a:** Alice's asset amount **A_b:** Bob's asset amount **s:** the secret used to create the hash lock **H(s):** the hash lock ```sequence Title: If everything goes as expected... participant Alice as A participant Apricot Chain as AC participant Banana Chain as BC participant Bob as B A -> BC: (1) setup BananaSwap with H(s) A -> AC: (2) setup ApricotSwap with H(s) Note over A, B: Entering premium escrowing phase A -> BC: (3) escrow P_a + P_b B -> AC: (4) escrow P_b Note over A, B: Entering asset escrowing phase A -> AC: (5) escrow A_a B -> BC: (6) escrow A_b Note over A, B: Entering redemption phase A -> BC: (7) redeem A_b with s & refund P_a + P_b B -> AC: (8) redeem A_a with s & refund P_b ``` #### Steps Explained **(1 & 2)** Alice must first set up the swap contracts. She will create a `BananaSwap` on the BananaChain and an `ApricotSwap` on the ApricotChain. Both will have the same hashLock $H(S)$, but only Alice knows the secret $S$. **Note:** A participant in this protocol escrows their premium on the chain of the asset they'd like to receive. That means that Alice and Bob must each own both assets. In this case, that means they both must own some `Apricot` and some `Banana`. **(3)** As the initiator, Alice has to escrow an amount equal to her premium + Bob's premium on the `BananaChain`. This might surprise you, as we'd expect her to only have to pay the amount totalling her premium. The reason for this is covered in Section 5.2 of the [**original paper**](https://arxiv.org/pdf/2105.06322.pdf). **(4)** Having seen Alice successfully escrow her premium on the `BananaChain`, Bob now escrows his premium on the `ApricotChain`. **(5)** Now that both parties have escrowed their premiums, we enter the asset escrowing phase. If either party disappears at this point, they'll lose their premium! Alice escrows her Apricots on the `ApricotChain`. **(6)** Having seen Alice correctly escrow her Apricots, Bob follows suit by escrowing his Bananas on the `BananaChain`. **(7)** Alice sees that Bob's bananas are correctly escrowed, so now she gets to redeem them! Doing so requires her to reveal the secret $S$. She should also get her premium refunded, since she followed the specifications of the protocol. **(8)** Since Alice had to reveal $S$ to collect her Bananas, Bob now knows the secret! Using $S$, he can collect his Apricots and have his premium P_b refunded. **FIN.** ```sequence Title: If Bob disappers... participant Alice as A participant Apricot Chain as AC participant Banana Chain as BC participant Bob as B A -> BC: (1) setup BananaSwap with H(s) A -> AC: (2) setup ApricotSwap with H(s) Note over A, B: Entering premium escrowing phase A -> BC: (3) escrow P_a + P_b B -> AC: (4) escrow P_b Note over A, B: Entering asset escrowing phase A -> AC: (5) escrow A_a B --> BC: (6) ZZZ ZZZ ZZZ Note over A, B: Entering redemption phase A -> AC: (7) redeem P_b & refund A_a A -> BC: (8) refund P_b + P_a ``` #### Steps Explained **(1-5)** Ommited. See explanations above. **(6)** Bob is nowhere to be found. He must be sleeping... **(7)** Since Bob missed his deadline to escrow his asset, Alice collects his premium deposited on the `ApricotChain`. She will also get her asset refunded. **(8)** Alice must also get her own premium refunded, which was escrowed on the `BananaChain` **FIN.** ```sequence Title: Bob never escrows his premium... participant Alice as A participant Apricot Chain as AC participant Banana Chain as BC participant Bob as B A -> BC: (1) setup BananaSwap with H(s) A -> AC: (2) setup ApricotSwap with H(s) Note over A, B: Entering premium escrowing phase A -> BC: (3) escrow P_a + P_b B -> AC: (4) ZZZ ZZZ Note over A, B: Entering redemption phase A -> BC: (5) refund P_b + P_a ``` **(1-3)** Ommited. See explanations above. **(4)** Bob is nowhere to be found. He must be sleeping... **(5)** Shoot! Alice doesn't get anything from this interaction. All she can do is get her initial premium refunded. She won't be doing any business with Bob again... **FIN.** ## Assignment :::info **Important caveat:** The protocol above uses two distinct chains, but we're only using Solidity and the Ethereum chain for this project. Ethereum has the best testing support, and it prevents you from having to learn multiple languages for this project. We'll be implementing all of the various calls above, since a person could be either the initiator or follower on either chain. While **in theory** you're implementing a **cross-chain swap**, **in reality** you're implementing an **ERC20 swap** with a protocol that would work across distinct chains. ::: For this assignment, you will implement the functions listed below. We recommend that you tackle them in the following order: 1. `setup` 2. `escrowPremium` 3. `escrowAsset` 4. `redeemAsset` 5. `refundAsset` 6. `redeemPremium` 7. `refundPremium` All functions are worth (roughly) the same amount. Check out our [grading breakdown](##Grading) for more info. #### Implement: ```solidity= /** setup is called to initialize an instance of a swap in this contract. Due to storage constraints, the various parts of the swap are spread out between the three different mappings above: swaps, assets, and premiums. */ function setup( uint expectedAssetEscrow, uint expectedPremiumEscrow, address payable assetEscrower, address payable premiumEscrower, address assetName, bytes32 hashLock, uint startTime, bool firstAssetEscrow, uint delta ) public payable canSetup(hashLock) {} ``` #### Overview * This function instantiates an instance of a swap (for one side of the protocol). * We recommend that you put all of your require statements in the `canSetup` modifier, which is currently blank. You should require that both the `assetEscrower` and the `premiumEscrower` for the swap that the hashLock maps to are the zero address. Otherwise, this swap has already been initiated. * You'll want to instantiate the appropriate structs based on the arguments and then add those structs to their mappings. * So what are the deadlines/timeouts that we have to set? That depends on whether that chain has the first asset escrow. The `delta` argument is an agreed-upon safe time interval for both parties, such that each interval is some `n * delta`, beginning from the `startTime`. * In the first protocol sequence above, `firstAssetEscrow` is true on the `ApricotChain` and false on the `BananaChain` (since in the asset escrowing round the Apricots are escrowed prior to the Bananas). Thus, the deadline for the premium on the `BananaChain` (where `firstAssetEscrow` is false) is `startTime + 1 * delta`. * The timeout for the swap is a time after which all possible events have occurred on a chain. Once again, that depends on the value of `firstAssetEscrow`. When can no more function calls be made when `firstAssetEscrow` is true? Hint: look at the ApricotChain. * Emit a SetUp event. #### (Somewhat Hidden) Helpful Fields: * `msg.sender`: the function's caller. #### Implement: ```solidity= /** The premium escrower has to escrow their premium for the protocol to succeed. */ function escrowPremium(bytes32 hashLock) public payable canEscrowPremium(hashLock) {} ``` #### Overview * Before you begin, you should require a few things to be true: is the call made in time? Is the right person making the call? Has the premium already been escrowed? Does the sender have enough of the token to make the call? * You'll want to transfer the expected amount of the premium from the premiumEscrower to the contract. * Set the field in this swap's premium struct demonstrating you've taken this step. * Emit the appropriate event. #### Helpful Functions * ERC20's `transfer` or `transferFrom`? Go back to HW 3 for a refresher. * ERC20's `balanceOf` #### Implement: ```solidity= /** The asset escrower has to escrow their premium for the protocol to succeed. */ function escrowAsset(bytes32 hashLock) public payable canEscrowAsset(hashLock) {} ``` #### Overview * You'll want to require all of the same things that you did in `escrowPremium` (except this time from the asset perspective). In addition, you'll want to require that the premium has been deposited. * You'll want to transfer the expected amount of the asset from the assetEscrower to the contract. * Set the field in this swap's asset struct demonstrating you've taken this step. * Emit the appropriate event. #### Implement: ```solidity= /** redeemAsset redeems the asset for the new owner. */ function redeemAsset(bytes32 preimage, bytes32 hashLock) public canRedeemAsset(preimage, hashLock) {} ``` #### Overview * At this point, you get the drift. What should we require? * Is a token being moved? If so, between which two parties? * What do we need to (re)set to show we've taken this step? * Emit the appropriate event. #### Helpful Functions * `sha256(abi.encode(message))`: how to get the SHA-256 hash of `message` #### Implement: ```solidity= /** refundPremium refunds the premiumEscrower's premium should the counterparty break from the protocol */ function refundPremium(bytes32 hashLock) public canRefundPremium(hashLock) {} ``` #### Overview * What should we require? For this and all following functions, we're going to require that it occurs AFTER something has occured instead of BEFORE. * Is a token being moved? If so, between which two parties? * What do we need to (re)set to show we've taken this step? * Emit the appropriate event. #### Implement: ```solidity= /** refundAsset refunds the asset to its original owner should the swap fail */ function refundAsset(bytes32 hashLock) public canRefundAsset(hashLock) {} ``` #### Overview * What should we require? * Is a token being moved? If so, between which two parties? * What do we need to (re)set to show we've taken this step? * Emit the appropriate event. #### Implement: ```solidity= /** redeemPremium allows a party to redeem the counterparty's premium should the swap fail */ function redeemPremium(bytes32 hashLock) public canRedeemPremium(hashLock) {} ``` #### Overview * What should we require? * Is a token being moved? If so, between which two parties? * What do we need to (re)set to show we've taken this step? * Emit the appropriate event. ## Testing This assignment is autograded, and you are able to run our test suite as many time as you like. While you can run all of your tests on Gradescope, we strongly recommend that you test locally. That way, you can fail quickly and try again! Hardhat allows us to log solidity variables, something that you cannot do normally. This means that you can use `console.log(message)` in your `.sol` files to log a message to standard output. This will save you a lot of frustration! #### Testing Resources: * [Solidity Documentation](https://docs.soliditylang.org/en/v0.8.12/) * [Solidity By Example](https://solidity-by-example.org/) * [Hardhat Documentation](https://hardhat.org/getting-started/) * [Testing with Hardhat](https://hardhat.org/tutorial/testing-contracts.html) * [Remix Documentation](https://remix-ide.readthedocs.io/en/latest/) ## Install :::warning This assignment requires a few steps prior to cloning the stencil. Please make sure you do those first! ::: 1. Clone the [stencil repo](https://classroom.github.com/a/_EH9OZXV). Since this a group assignment, **make sure that you know who your partner is before you accept!** Otherwise, things might get a little confusing. 2. To install the package dependencies, run `npm i` 3. Get after it! Good luck :smiley: ## HandIn - Log into [Gradescope](https://www.gradescope.com/) and Submit your code using **GitHub**. ## Grading :::warning **Note:** This assignment's feedback form is a **REQUIRED** part of the assignment, worth 5 points. You can find that form [**here**](https://forms.gle/dVwVsiWCU9yyAXk86). ::: This assignment is out of **100 points**. Each test suite below consists of several tests. You must pass all of the tests in the suite to receive credit. No partial credit will be awarded. On Gradescope, each individual test is worth 1 point, for 38 total points. It was much easier to set up that way, so we'll adjust the scores later to match the rubric below. ### Rubric | Test Suite | Points | |:---------------- |:-------:| | `Setup` | 14 | | `Escrow Premium` | 13 | | `Escrow Asset` | 14 | | `Redeem Asset` | 14 | | `Refund Asset` | 14 | | `Redeem Premium` | 13 | | `Refund Premium` | 13 | | Feedback Form | 5 | | **Total** | **100** |