Skip to content

Instantly share code, notes, and snippets.

@RadNi
Created August 27, 2024 18:06
Show Gist options
  • Save RadNi/703def6ae3f927392056024f57b30362 to your computer and use it in GitHub Desktop.
Save RadNi/703def6ae3f927392056024f57b30362 to your computer and use it in GitHub Desktop.
An implementation of atomic swaps in the async programing model
pragma solidity ^0.8.0;
import "./Nil.sol";
/*
An implementation of atomic swaps in the async programing model
*/
contract Swap is NilBase{
// Any instance of swap requests are held in an `escrow` struct
struct escrow {
mapping(uint256 => Nil.Token) firstDebtTokens; // The first party's tokens to be deposited
mapping(uint256 => Nil.Token) secondDebtTokens; // The second party's tokens to be deposited
mapping(uint256 => uint256) firstAmounts; // The amount of each token that the first party has already deposited
mapping(uint256 => uint256) secondAmounts; // The amount of each token that the second party has already deposited
uint256 firstTokensSize; // Number of tokens to be deposited by the first party
uint256 secondTokensSize; // Number of tokens to be deposited by the second party
address firstParty; // First party's address
address secondParty; // Second party's address
bool isValid; // It's set to false if the escrow doesn't exist, or it's already finalized
}
mapping(uint256 => escrow) public requests;
uint256 public counter = 0;
event NewEscrow(uint256 indexed reqId, address indexed, address indexed);
event Deposit(uint256 indexed reqId, bool indexed party, uint256 tokenId, uint256 amount);
event SwapFinalized(uint256 indexed reqId, address indexed first, address indexed second);
// A swap request can be initiated by everyone. It determines parties addresses, the tokens involved in each side of the swap along with their required values.
// Note that, one should send tokens to this function.
// Upon successful registration, it emits a `NewEscrow` log
function newRequest(
address first, address second,
uint256[] memory debtsFirstIds, uint256[] memory debtsFirstAmount,
uint256[] memory debtsSecondIds, uint256[] memory debtsSecondAmount
) public returns (uint256){
require(debtsFirstIds.length == debtsFirstAmount.length, "array lengths do not match!");
require(debtsSecondIds.length == debtsSecondAmount.length, "array lengths do not match!");
// filling in the request's data
counter += 1;
requests[counter].isValid = true;
requests[counter].firstParty = first;
requests[counter].secondParty = second;
requests[counter].firstTokensSize = debtsFirstIds.length;
requests[counter].secondTokensSize = debtsSecondIds.length;
// filling in the tokens' data
for (uint256 i = 0; i < debtsFirstIds.length; i++) {
requests[counter].firstAmounts[debtsFirstIds[i]] = 0;
requests[counter].firstDebtTokens[i].id = debtsFirstIds[i];
requests[counter].firstDebtTokens[i].amount = debtsFirstAmount[i];
}
for (uint256 i = 0; i < debtsSecondIds.length; i++) {
requests[counter].secondAmounts[debtsFirstIds[i]] = 0;
requests[counter].secondDebtTokens[i].id = debtsSecondIds[i];
requests[counter].secondDebtTokens[i].amount = debtsSecondAmount[i];
}
// emit log and return the request id
emit NewEscrow(counter, first, second);
return counter;
}
// is used to deposit tokens into a particular escrow
function deposit(uint256 reqId, bool party) public {
require(requests[reqId].isValid, "Escrow doesn't exist!");
Nil.Token[] memory tokens = Nil.msgTokens();
for (uint256 i = 0; i < tokens.length; i++) {
if (party) { // First party
requests[reqId].firstAmounts[tokens[i].id] += tokens[i].amount;
} else { // Second party
requests[reqId].secondAmounts[tokens[i].id] += tokens[i].amount;
}
emit Deposit(reqId, party, tokens[i].id, tokens[i].amount);
}
}
// once all the required tokens are fulfilled, everyone can finalize the swap which sends out the exchanged values to either parties
function finalize(uint256 reqId) public {
require(requests[reqId].isValid, "Escrow doesn't exist!");
for (uint256 index = 0; index < requests[reqId].firstTokensSize; index++) {
uint256 max = requests[reqId].firstDebtTokens[index].amount;
uint256 tId = requests[reqId].firstDebtTokens[index].id;
uint256 amount = requests[reqId].firstAmounts[tId];
require(amount >= max, "request not fulfiled on the first side yet!");
}
for (uint256 index = 0; index < requests[reqId].secondTokensSize; index++) {
uint256 max = requests[reqId].secondDebtTokens[index].amount;
uint256 tId = requests[reqId].secondDebtTokens[index].id;
uint256 amount = requests[reqId].secondAmounts[tId];
require(amount >= max, "request not fulfiled on the second side yet!");
}
requests[reqId].isValid = false;
Nil.Token[] memory firstTokens = new Nil.Token[] (requests[reqId].firstTokensSize);
Nil.Token[] memory secondTokens = new Nil.Token[] (requests[reqId].secondTokensSize);
for (uint256 index = 0; index < requests[reqId].firstTokensSize; index++) {
firstTokens[index].amount = requests[reqId].secondDebtTokens[index].amount;
firstTokens[index].id = requests[reqId].secondDebtTokens[index].id;
}
for (uint256 index = 0; index < requests[reqId].secondTokensSize; index++) {
secondTokens[index].amount = requests[reqId].firstDebtTokens[index].amount;
secondTokens[index].id = requests[reqId].firstDebtTokens[index].id;
}
Nil.asyncCall(
requests[reqId].firstParty,
address(0),
address(0),
50,
1,
false,
0,
secondTokens,
""
);
Nil.asyncCall(
requests[reqId].secondParty,
address(0),
address(0),
50,
1,
false,
0,
firstTokens,
""
);
emit SwapFinalized(reqId, requests[reqId].firstParty, requests[reqId].secondParty);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment