Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into fix/tob-garden-9
Browse files Browse the repository at this point in the history
  • Loading branch information
r4reetik committed Jun 18, 2024
2 parents fab9879 + 7f3b2fc commit c70eee7
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 32 deletions.
12 changes: 8 additions & 4 deletions contracts/fee/GardenFEEAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract GardenFEEAccount is EIP712Upgradeable {

struct HTLC {
bytes32 secretHash;
uint256 timeLock;
uint256 expiry;
uint256 sendAmount;
uint256 recieveAmount;
}
Expand All @@ -37,12 +37,12 @@ contract GardenFEEAccount is EIP712Upgradeable {
keccak256(
abi.encodePacked(
"Claim(uint256 nonce,uint256 amount,HTLC[] htlcs)",
"HTLC(bytes32 secretHash,uint256 timeLock,uint256 sendAmount,uint256 recieveAmount)"
"HTLC(bytes32 secretHash,uint256 expiry,uint256 sendAmount,uint256 recieveAmount)"
)
);

bytes32 private constant HTLC_TYPEHASH =
keccak256("HTLC(bytes32 secretHash,uint256 timeLock,uint256 sendAmount,uint256 recieveAmount)");
keccak256("HTLC(bytes32 secretHash,uint256 expiry,uint256 sendAmount,uint256 recieveAmount)");

// Are set when the channel is created
IERC20Upgradeable public token;
Expand All @@ -60,6 +60,10 @@ contract GardenFEEAccount is EIP712Upgradeable {

uint256 private constant TWO_DAYS = 2 * 7200;

function initialize() initializer external {
_disableInitializers();
}

function __GardenFEEAccount_init(
IERC20Upgradeable token_,
address funder_,
Expand Down Expand Up @@ -211,7 +215,7 @@ contract GardenFEEAccount is EIP712Upgradeable {
abi.encode(
HTLC_TYPEHASH,
htlcs[i].secretHash,
htlcs[i].timeLock,
htlcs[i].expiry,
htlcs[i].sendAmount,
htlcs[i].recieveAmount
)
Expand Down
8 changes: 6 additions & 2 deletions contracts/fee/GardenFEEAccountFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ contract GardenFEEAccountFactory {
) {
token = token_;
feeManager = feeManager_;
template = address(new GardenFEEAccount());

feeAccountName = feeAccountName_;
feeAccountVersion = feeAccountVersion_;

GardenFEEAccount templateFeeAccount = new GardenFEEAccount();
templateFeeAccount.initialize();
template = address(templateFeeAccount);
}

/**
Expand Down Expand Up @@ -107,7 +111,7 @@ contract GardenFEEAccountFactory {
delete channels[recipient];
}

/**
/**
* @notice Creates a fee channel.
* @dev The fee channel is created by deploying a clone using the template.
* This function is only callable by the fee manager.
Expand Down
64 changes: 44 additions & 20 deletions contracts/htlc/GardenHTLC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract GardenHTLC is EIP712 {
address initiator;
address redeemer;
uint256 initiatedAt;
uint256 expiry;
uint256 timelock;
uint256 amount;
}

Expand All @@ -33,7 +33,9 @@ contract GardenHTLC is EIP712 {
mapping(bytes32 => Order) public orders;

bytes32 private constant _INITIATE_TYPEHASH =
keccak256("Initiate(address redeemer,uint256 expiry,uint256 amount,bytes32 secretHash)");
keccak256("Initiate(address redeemer,uint256 timelock,uint256 amount,bytes32 secretHash)");

bytes32 private constant _REFUND_TYPEHASH = keccak256("Refund(bytes32 orderId)");

event Initiated(bytes32 indexed orderID, bytes32 indexed secretHash, uint256 amount);
event Redeemed(bytes32 indexed orderID, bytes32 indexed secretHash, bytes secret);
Expand All @@ -43,19 +45,19 @@ contract GardenHTLC is EIP712 {
* @notice .
* @dev provides checks to ensure
* 1. redeemer is not null address
* 3. expiry is greater than current block number
* 3. timelock is greater than 0
* 4. amount is not zero
* @param redeemer public address of the reedeem
* @param expiry expiry in period for the htlc order
* @param timelock timelock in period for the htlc order
* @param amount amount of tokens to trade
*/
modifier safeParams(
address redeemer,
uint256 expiry,
uint256 timelock,
uint256 amount
) {
require(redeemer != address(0), "GardenHTLC: zero address redeemer");
require(expiry > 0, "GardenHTLC: zero expiry");
require(timelock > 0, "GardenHTLC: zero timelock");
require(amount > 0, "GardenHTLC: zero amount");
_;
}
Expand All @@ -70,17 +72,17 @@ contract GardenHTLC is EIP712 {
* and sha256 hash should be used to support hashing methods on other non-evm chains.
* Signers cannot generate orders with same secret hash or override an existing order.
* @param redeemer public address of the redeemer
* @param expiry expiry in period for the htlc order
* @param timelock timelock in period for the htlc order
* @param amount amount of tokens to trade
* @param secretHash sha256 hash of the secret used for redemtion
**/
function initiate(
address redeemer,
uint256 expiry,
uint256 timelock,
uint256 amount,
bytes32 secretHash
) external safeParams(redeemer, expiry, amount) {
_initiate(msg.sender, redeemer, expiry, amount, secretHash);
) external safeParams(redeemer, timelock, amount) {
_initiate(msg.sender, redeemer, timelock, amount, secretHash);
}

/**
Expand All @@ -89,23 +91,23 @@ contract GardenHTLC is EIP712 {
* and sha256 hash should be used to support hashing methods on other non-evm chains.
* Signers cannot generate orders with same secret hash or override an existing order.
* @param redeemer public address of the redeemer
* @param expiry expiry in period for the htlc order
* @param timelock timelock in period for the htlc order
* @param amount amount of tokens to trade
* @param secretHash sha256 hash of the secret used for redemtion
* @param signature EIP712 signature provided by authorized user for iniatiation. user will be assigned as initiator
**/
function initiateWithSignature(
address redeemer,
uint256 expiry,
uint256 timelock,
uint256 amount,
bytes32 secretHash,
bytes calldata signature
) external safeParams(redeemer, expiry, amount) {
) external safeParams(redeemer, timelock, amount) {
address initiator = _hashTypedDataV4(
keccak256(abi.encode(_INITIATE_TYPEHASH, redeemer, expiry, amount, secretHash))
keccak256(abi.encode(_INITIATE_TYPEHASH, redeemer, timelock, amount, secretHash))
).recover(signature);

_initiate(initiator, redeemer, expiry, amount, secretHash);
_initiate(initiator, redeemer, timelock, amount, secretHash);
}

/**
Expand Down Expand Up @@ -134,7 +136,7 @@ contract GardenHTLC is EIP712 {
}

/**
* @notice Signers can refund the locked assets after expiry block number
* @notice Signers can refund the locked assets after timelock block number
* @dev Signers cannot refund the an order before epiry block number or refund the same order
* multiple times.
* Funds will be SafeTransferred to the initiator.
Expand All @@ -145,7 +147,7 @@ contract GardenHTLC is EIP712 {

require(order.redeemer != address(0), "GardenHTLC: order not initiated");
require(!order.isFulfilled, "GardenHTLC: order fulfilled");
require(order.initiatedAt + order.expiry < block.number, "GardenHTLC: order not expired");
require(order.initiatedAt + order.timelock < block.number, "GardenHTLC: order not expired");

order.isFulfilled = true;

Expand All @@ -164,13 +166,13 @@ contract GardenHTLC is EIP712 {
* @param initiator_ The address of the initiator of the atomic swap
* @param redeemer_ The address of the redeemer of the atomic swap
* @param secretHash_ The hash of the secret used for redemption
* @param expiry_ The expiry block number for the atomic swap
* @param timelock_ The timelock block number for the atomic swap
* @param amount_ The amount of tokens to be traded in the atomic swap
*/
function _initiate(
address initiator_,
address redeemer_,
uint256 expiry_,
uint256 timelock_,
uint256 amount_,
bytes32 secretHash_
) internal {
Expand All @@ -186,7 +188,7 @@ contract GardenHTLC is EIP712 {
initiator: initiator_,
redeemer: redeemer_,
initiatedAt: block.number,
expiry: expiry_,
timelock: timelock_,
amount: amount_
});
orders[orderID] = newOrder;
Expand All @@ -195,4 +197,26 @@ contract GardenHTLC is EIP712 {

token.safeTransferFrom(initiator_, address(this), orders[orderID].amount);
}

/**
* @notice Redeemers can let initiator refund the locked assets before expiry block number
* @dev Signers cannot refund the the same order multiple times.
* Funds will be SafeTransferred to the initiator.
*
* @param orderID orderID of the htlc order
* @param signature EIP712 signature provided by redeemer for instant refund.
*/
function instantRefund(bytes32 orderID, bytes calldata signature) external {
address redeemer = _hashTypedDataV4(keccak256(abi.encode(_REFUND_TYPEHASH, orderID))).recover(signature);
Order storage order = orders[orderID];

require(order.redeemer == redeemer, "HTLC: invalid redeemer signature");
require(!order.isFulfilled, "HTLC: order fulfilled");

order.isFulfilled = true;

emit Refunded(orderID);

token.safeTransfer(order.initiator, order.amount);
}
}
4 changes: 3 additions & 1 deletion contracts/stake/DelegateManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ abstract contract DelegateManager is BaseStaker {
require(stake.expiry < block.number, "DelegateManager: stake not expired");

uint8 multiplier = _calculateVoteMultiplier(newLockBlocks);
stake.expiry = block.number + newLockBlocks;
stake.expiry = multiplier == uint8(7) ? MAX_UINT_256 : block.number + newLockBlocks;
stake.votes = multiplier * stake.units;

stakes[stakeID] = stake;
Expand Down Expand Up @@ -175,6 +175,8 @@ abstract contract DelegateManager is BaseStaker {
* @return voteCount The total number of votes delegated to the specified filler address.
*/
function getVotes(address filler) external view returns (uint256 voteCount) {
_checkRole(FILLER, filler);

bytes32[] memory delegates = fillers[filler].delegateStakeIDs._inner._values;
uint256 delegateLength = delegates.length;

Expand Down
4 changes: 3 additions & 1 deletion contracts/stake/FillerManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ abstract contract FillerManager is BaseStaker {
require(filler.deregisteredAt != 0, "FillerManager: not registered");
require(filler.deregisteredAt + FILLER_COOL_DOWN < block.number, "FillerManager: cooldown not passed");

delete (fillers[filler_]);
fillers[filler_].feeInBips = 0;
fillers[filler_].stake = 0;
fillers[filler_].deregisteredAt = 0;

SEED.safeTransfer(filler_, FILLER_STAKE);

Expand Down
72 changes: 68 additions & 4 deletions test/htlc/gardenHTLC.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("--- HTLC ---", () => {
secretHash: BytesLike;
};

const TYPES: Record<string, TypedDataField[]> = {
const INITIATE_TYPE: Record<string, TypedDataField[]> = {
Initiate: [
{ name: "redeemer", type: "address" },
{ name: "expiry", type: "uint256" },
Expand All @@ -30,6 +30,10 @@ describe("--- HTLC ---", () => {
],
};

const REFUND_TYPE: Record<string, TypedDataField[]> = {
Refund: [{ name: "orderId", type: "bytes32" }],
};

let owner: HardhatEthersSigner;
let alice: HardhatEthersSigner;
let bob: HardhatEthersSigner;
Expand All @@ -45,12 +49,14 @@ describe("--- HTLC ---", () => {
let secret3: BytesLike;
let secret4: BytesLike;
let secret5: BytesLike;
let secret6: BytesLike;

let orderID1: BytesLike;
let orderID2: BytesLike;
let orderID3: BytesLike;
let orderID4: BytesLike;
let orderID5: BytesLike;
let orderID6: BytesLike;

let expiry_: BigNumberish;

Expand All @@ -74,6 +80,7 @@ describe("--- HTLC ---", () => {
secret3 = randomBytes(32);
secret4 = randomBytes(32);
secret5 = randomBytes(32);
secret6 = randomBytes(32);
});

describe("- Pre-Conditions -", () => {
Expand Down Expand Up @@ -359,7 +366,7 @@ describe("--- HTLC ---", () => {
secretHash: ethers.sha256(secret5),
};

const signature = await bob.signTypedData(DOMAIN, TYPES, initiate);
const signature = await bob.signTypedData(DOMAIN, INITIATE_TYPE, initiate);

await expect(
gardenHTLC
Expand All @@ -383,7 +390,7 @@ describe("--- HTLC ---", () => {
secretHash: ethers.sha256(secret5),
};

const signature = await alice.signTypedData(DOMAIN, TYPES, initiate);
const signature = await alice.signTypedData(DOMAIN, INITIATE_TYPE, initiate);

await expect(
gardenHTLC
Expand Down Expand Up @@ -415,7 +422,7 @@ describe("--- HTLC ---", () => {
)
);

const signature = await alice.signTypedData(DOMAIN, TYPES, initiate);
const signature = await alice.signTypedData(DOMAIN, INITIATE_TYPE, initiate);

await expect(
gardenHTLC
Expand Down Expand Up @@ -533,4 +540,61 @@ describe("--- HTLC ---", () => {
expect(await seed.balanceOf(alice.address)).to.equal(ethers.parseEther("500"));
});
});

describe("- HTLC - Instant Refund -", () => {
let instantRefund: {
orderId: string;
};
it("Should not able to instant refund a swap with an invalid signature.", async () => {
expiry_ = (await ethers.provider.getBlockNumber()) + 7200;
const initiate: Initiate = {
redeemer: bob.address,
expiry: expiry_,
amount: ethers.parseEther("100"),
secretHash: ethers.sha256(secret6),
};

orderID6 = ethers.sha256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "address"],
[initiate.secretHash, alice.address]
)
);
await expect(
gardenHTLC
.connect(alice)
.initiate(
initiate.redeemer,
initiate.expiry,
initiate.amount,
initiate.secretHash
)
)
.to.emit(gardenHTLC, "Initiated")
.withArgs(orderID6, initiate.secretHash, initiate.amount);

instantRefund = { orderId: orderID6 };

const instantRefundSig = await alice.signTypedData(
DOMAIN,
REFUND_TYPE,
instantRefund
);

await expect(
gardenHTLC.connect(charlie).instantRefund(orderID6, instantRefundSig)
).to.be.revertedWith("HTLC: invalid redeemer signature");
});
it("Should be able to instant refund a swap with an valid signature.", async () => {
const instantRefundSig = await bob.signTypedData(
DOMAIN,
REFUND_TYPE,
instantRefund
);

await expect(gardenHTLC.connect(charlie).instantRefund(orderID6, instantRefundSig))
.to.emit(gardenHTLC, "Refunded")
.withArgs(orderID6);
});
});
});

0 comments on commit c70eee7

Please sign in to comment.