Skip to content

Commit

Permalink
Merge branch 'master' into nmlinaric/permissionless-token-adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
nmlinaric authored Aug 4, 2024
2 parents 632a273 + 27d9682 commit f333a64
Show file tree
Hide file tree
Showing 18 changed files with 1,313 additions and 23 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## [2.9.0](https://github.com/sygmaprotocol/sygma-solidity/compare/v2.8.0...v2.9.0) (2024-07-30)


### Features

* add native token handler and adapter ([#254](https://github.com/sygmaprotocol/sygma-solidity/issues/254)) ([d5cea35](https://github.com/sygmaprotocol/sygma-solidity/commit/d5cea359a5304113e10283bd5ea4e4070284b6d9))


### Bug Fixes

* make frost keygen retryable ([#251](https://github.com/sygmaprotocol/sygma-solidity/issues/251)) ([88fd294](https://github.com/sygmaprotocol/sygma-solidity/commit/88fd29458afe10ae2ee8994e992570b7d668c946))


### Miscellaneous

* update what gets pushed to npm package ([#258](https://github.com/sygmaprotocol/sygma-solidity/issues/258)) ([faa73ae](https://github.com/sygmaprotocol/sygma-solidity/commit/faa73aea87a1b3bd228f652481733d1592fc49f6))

## [2.8.0](https://github.com/sygmaprotocol/sygma-solidity/compare/v2.7.0...v2.8.0) (2024-06-07)


Expand Down
25 changes: 16 additions & 9 deletions contracts/FROSTKeygen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ import "@openzeppelin/contracts/access/Ownable.sol";

contract FROSTKeygen is Ownable {

bool private keygenStarted;
event StartedFROSTKeygen();
bool private keygenEnded = false;

event StartedFROSTKeygen();
event EndedFROSTKeygen();

modifier onlyOnce(){
require (!keygenStarted, "FROST keygen can be called only once");
_;
keygenStarted = true;
}

/**
@notice Emits {StartedFROSTKeygen} event
*/
function startFROSTKeygen() public onlyOwner onlyOnce {
function startFROSTKeygen() public onlyOwner {
require (!keygenEnded, "FROST keygen ended");

emit StartedFROSTKeygen();
}

/**
@notice Blocks further calls for starting keygen.
*/
function endFROSTKeygen() public onlyOwner {
keygenEnded = true;

emit EndedFROSTKeygen();
}

}
24 changes: 24 additions & 0 deletions contracts/Retry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only

pragma solidity 0.8.11;

import "@openzeppelin/contracts/access/Ownable.sol";

contract Retry is Ownable {

event Retry(uint8 sourceDomainID, uint8 destinationDomainID, uint256 blockHeight, bytes32 resourceID);

/**
@notice This method is used to trigger the process for retrying failed deposits on the MPC side.
@notice Only callable by admin.
@param sourceDomainID ID of the retry source.
@param destinationDomainID ID of the transfer destination.
@param blockHeight Block height on origin chain which contains failed deposits.
@param resourceID Resource ID of transfers that are to be retried.
*/
function retry(uint8 sourceDomainID, uint8 destinationDomainID, uint256 blockHeight, bytes32 resourceID) external onlyOwner {
emit Retry(sourceDomainID, destinationDomainID, blockHeight, resourceID);
}

}
62 changes: 62 additions & 0 deletions contracts/adapters/nativeTokens/NativeTokenAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.11;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "../../interfaces/IBridge.sol";
import "../../interfaces/IFeeHandler.sol";


contract NativeTokenAdapter is AccessControl {
IBridge public immutable _bridge;
bytes32 public immutable _resourceID;

event Withdrawal(address recipient, uint amount);

error SenderNotAdmin();
error InsufficientMsgValueAmount(uint256 amount);
error MsgValueLowerThanFee(uint256 amount);
error TokenWithdrawalFailed();
error InsufficientBalance();
error FailedFundsTransfer();

modifier onlyAdmin() {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert SenderNotAdmin();
_;
}

constructor(address bridge, bytes32 resourceID) {
_bridge = IBridge(bridge);
_resourceID = resourceID;
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

function deposit(uint8 destinationDomainID, string calldata recipientAddress) external payable {
if (msg.value <= 0) revert InsufficientMsgValueAmount(msg.value);
address feeHandlerRouter = _bridge._feeHandler();
(uint256 fee, ) = IFeeHandler(feeHandlerRouter).calculateFee(
address(this),
_bridge._domainID(),
destinationDomainID,
_resourceID,
"", // depositData - not parsed
"" // feeData - not parsed
);

if (msg.value < fee) revert MsgValueLowerThanFee(msg.value);
uint256 transferAmount = msg.value - fee;

bytes memory depositData = abi.encodePacked(
transferAmount,
bytes(recipientAddress).length,
recipientAddress
);

_bridge.deposit{value: fee}(destinationDomainID, _resourceID, depositData, "");

address nativeHandlerAddress = _bridge._resourceIDToHandlerAddress(_resourceID);
(bool success, ) = nativeHandlerAddress.call{value: transferAmount}("");
if(!success) revert FailedFundsTransfer();
}

receive() external payable {}
}
2 changes: 1 addition & 1 deletion contracts/handlers/FeeHandlerRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract FeeHandlerRouter is IFeeHandler, AccessControl {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
@notice Maps the {handlerAddress} to {resourceID} to {destinantionDomainID} in {_domainResourceIDToFeeHandlerAddress}.
@notice Maps the {handlerAddress} to {resourceID} to {destinationDomainID} in {_domainResourceIDToFeeHandlerAddress}.
@param destinationDomainID ID of chain FeeHandler contracts will be called.
@param resourceID ResourceID for which the corresponding FeeHandler will collect/calcualte fee.
@param handlerAddress Address of FeeHandler which will be called for specified resourceID.
Expand Down
124 changes: 124 additions & 0 deletions contracts/handlers/NativeTokenHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

import "../interfaces/IHandler.sol";
import "./ERCHandlerHelpers.sol";

/**
@title Handles native token deposits and deposit executions.
@author ChainSafe Systems.
@notice This contract is intended to be used with the Bridge contract.
*/
contract NativeTokenHandler is IHandler, ERCHandlerHelpers {

address public immutable _nativeTokenAdapterAddress;

/**
@param bridgeAddress Contract address of previously deployed Bridge.
*/
constructor(
address bridgeAddress,
address nativeTokenAdapterAddress
) ERCHandlerHelpers(bridgeAddress) {
_nativeTokenAdapterAddress = nativeTokenAdapterAddress;
}

event Withdrawal(address recipient, uint256 amount);
event FundsTransferred(address recipient, uint256 amount);

error FailedFundsTransfer();
error InsufficientBalance();
error InvalidSender(address sender);

/**
@notice A deposit is initiated by making a deposit to the NativeTokenAdapter which constructs the required
deposit data and propagates it to the Bridge contract.
@param resourceID ResourceID used to find address of token to be used for deposit.
@param depositor Address of account making the deposit in the Bridge contract.
@param data Consists of {amount} padded to 32 bytes.
@notice Data passed into the function should be constructed as follows:
amount uint256 bytes 0 - 32
destinationRecipientAddress length uint256 bytes 32 - 64
destinationRecipientAddress bytes bytes 64 - END
@return deposit amount internal representation.
*/
function deposit(
bytes32 resourceID,
address depositor,
bytes calldata data
) external override onlyBridge returns (bytes memory) {
uint256 amount;
(amount) = abi.decode(data, (uint256));

if(depositor != _nativeTokenAdapterAddress) revert InvalidSender(depositor);

address tokenAddress = _resourceIDToTokenContractAddress[resourceID];

return abi.encodePacked(convertToInternalBalance(tokenAddress, amount));
}

/**
@notice Proposal execution should be initiated when a proposal is finalized in the Bridge contract
by a relayer on the deposit's destination chain.
@param resourceID ResourceID to be used when making deposits.
@param data Consists of {amount}, {lenDestinationRecipientAddress}
and {destinationRecipientAddress}.
@notice Data passed into the function should be constructed as follows:
amount uint256 bytes 0 - 32
destinationRecipientAddress length uint256 bytes 32 - 64 // not used
destinationRecipientAddress bytes bytes 64 - 84
*/
function executeProposal(bytes32 resourceID, bytes calldata data) external override onlyBridge returns (bytes memory) {
(uint256 amount) = abi.decode(data, (uint256));
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
address recipientAddress = address(bytes20(bytes(data[64:84])));
uint256 convertedAmount = convertToExternalBalance(tokenAddress, amount);

(bool success, ) = address(recipientAddress).call{value: convertedAmount}("");
if(!success) revert FailedFundsTransfer();
emit FundsTransferred(recipientAddress, amount);

return abi.encode(tokenAddress, address(recipientAddress), convertedAmount);
}

/**
@notice Used to manually release ERC20 tokens from ERC20Safe.
@param data Consists of {tokenAddress}, {recipient}, and {amount} all padded to 32 bytes.
@notice Data passed into the function should be constructed as follows:
tokenAddress address bytes 0 - 32
recipient address bytes 32 - 64
amount uint bytes 64 - 96
*/
function withdraw(bytes memory data) external override onlyBridge {
address recipient;
uint amount;

if (address(this).balance <= amount) revert InsufficientBalance();
(, recipient, amount) = abi.decode(data, (address, address, uint));

(bool success, ) = address(recipient).call{value: amount}("");
if(!success) revert FailedFundsTransfer();
emit Withdrawal(recipient, amount);
}

/**
@notice Sets {_resourceIDToContractAddress} with {contractAddress},
{_tokenContractAddressToTokenProperties[tokenAddress].resourceID} with {resourceID} and
{_tokenContractAddressToTokenProperties[tokenAddress].isWhitelisted} to true for {contractAddress} in ERCHandlerHelpers contract.
Sets decimals value for contractAddress if value is provided in args.
@param resourceID ResourceID to be used when making deposits.
@param contractAddress Address of contract to be called when a deposit is made and a deposited is executed.
@param args Additional data passed to the handler - this should be 1 byte containing number of decimals places.
*/
function setResource(bytes32 resourceID, address contractAddress, bytes calldata args) external onlyBridge {
_setResource(resourceID, contractAddress);

if (args.length > 0) {
uint8 externalTokenDecimals = uint8(bytes1(args));
_setDecimals(contractAddress, externalTokenDecimals);
}
}

receive() external payable {}
}
4 changes: 2 additions & 2 deletions contracts/handlers/fee/BasicFeeHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ contract BasicFeeHandler is IFeeHandler, AccessControl {
}

/**
@notice Maps the {newFee} to {destinantionDomainID} to {resourceID} in {_domainResourceIDToFee}.
@notice Maps the {newFee} to {destinationDomainID} to {resourceID} in {_domainResourceIDToFee}.
@notice Only callable by admin.
@param destinationDomainID ID of chain fee will be set.
@param resourceID ResourceID for which fee will be set.
@param newFee Value to which fee will be updated to for the provided {destinantionDomainID} and {resourceID}.
@param newFee Value to which fee will be updated to for the provided {destinationDomainID} and {resourceID}.
*/
function changeFee(uint8 destinationDomainID, bytes32 resourceID, uint256 newFee) external onlyAdmin {
uint256 currentFee = _domainResourceIDToFee[destinationDomainID][resourceID];
Expand Down
11 changes: 11 additions & 0 deletions contracts/interfaces/IBasicFeeHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.11;

interface IBasicFeeHandler {

/**
@notice Exposes getter function for _domainResourceIDToFee
*/
function _domainResourceIDToFee(uint8 destinationDomainID, bytes32 resourceID) pure external returns (uint256);
}
10 changes: 10 additions & 0 deletions migrations/10_retry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const RetryContract = artifacts.require("Retry");

module.exports = async function (deployer) {
await deployer.deploy(RetryContract);
const RetryInstance = await RetryContract.deployed();

console.table({
"Retry Address": RetryInstance.address,
});
}
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@buildwithsygma/sygma-contracts",
"version": "2.8.0",
"version": "2.9.0",
"description": "",
"main": "dist/index.js",
"repository": "https://github.com/sygmaprotocol/sygma-solidity.git",
Expand All @@ -11,11 +11,14 @@
"build/contracts/ERC721Handler.json",
"build/contracts/ERC1155Handler.json",
"build/contracts/GmpHandler.json",
"build/contract/FeeHandlerRouter.json",
"build/contracts/BasicFeeHandler.json",
"build/contracts/PercentageERC20FeeHandler.json",
"build/contracts/DynamicGenericFeeHandler.json",
"build/contracts/DynamicERC20FeeHandler.json",
"build/contracts/PercentageERC20FeeHandler.json",
"build/contracts/TwapOracle.json",
"build/contracts/TwapFeeHandler.json",
"build/contracts/FeeHandlerRouter.json",
"build/contracts/NativeTokenAdapter.json",
"build/contracts/NativeTokenHandler.json",
"contracts/interfaces"
],
"directories": {
Expand Down
Loading

0 comments on commit f333a64

Please sign in to comment.