Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ethereum contracts): ERC20Gateway ethereum contract #138

Merged
merged 10 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions ethereum/client/build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::process::Command;

fn main() {
println!("cargo::rerun-if-changed=../src");
println!("cargo::rerun-if-changed=../lib");
println!("cargo::rerun-if-changed=*");

Command::new("forge")
.arg("build")
Expand Down
19 changes: 0 additions & 19 deletions ethereum/client/src/Cargo.toml

This file was deleted.

74 changes: 74 additions & 0 deletions ethereum/src/ERC20Gateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {IERC20Gateway} from "./interfaces/IERC20Gateway.sol";
import {VFT_TREASURY_ADDRESS} from "./libraries/Environment.sol";
import {IMessageQueue, IMessageQueueReceiver, VaraMessage} from "./interfaces/IMessageQueue.sol";
import {ERC20VaraSupply} from "./ERC20VaraSupply.sol";

contract ERC20Gateway is IERC20Gateway, Context, IMessageQueueReceiver {
address immutable MESSAGE_QUEUE_ADDRESS;

constructor(address message_queue) {
MESSAGE_QUEUE_ADDRESS = message_queue;
}

/** @dev Request token bridging. When the bridging is requested tokens are burned
* from account that've sent transaction and `BridgingRequested` event is emitted that later can be verified
* on other side of bridge. Allowance needs to allow gateway contract transferring `amount` of tokens.
*
* @param token token address to transfer over bridge
* @param amount quantity of tokens to transfer over bridge
* @param to destination of transfer on VARA network
*/
function requestBridging(address token, uint256 amount, bytes32 to) public {
ERC20VaraSupply(token).burnFrom(_msgSender(), amount);
emit BridgingRequested(_msgSender(), to, token, amount);
}

/** @dev Accept bridging request made on other side of bridge.
* This request can be sent by `MessageQueue` only. When such a request is accpeted, tokens
* are minted to the corresponding account address, specified in `vara_msg`.
*
* Expected `payload` in `VaraMessage` consisits of these:
* - `receiver` - account to mint tokens to
* - `token` - token to mint
* - `amount` - amount of tokens to mint
*
* @param vara_msg `VaraMessage` received from MessageQueue.
*/
function processVaraMessage(
VaraMessage calldata vara_msg
) external returns (bool) {
uint160 receiver;
uint160 token;
uint256 amount;
if (msg.sender != MESSAGE_QUEUE_ADDRESS) {
revert NotAuthorized();
}
if (vara_msg.data.length != 20 + 20 + 32) {
revert BadArguments();
}
if (vara_msg.receiver != address(this)) {
revert BadEthAddress();
}
if (vara_msg.sender != VFT_TREASURY_ADDRESS) {
revert BadVaraAddress();
}

assembly {
receiver := shr(96, calldataload(0xC4))
token := shr(96, calldataload(0xD8))
amount := calldataload(0xEC)
}

ERC20VaraSupply(address(token)).mint(address(receiver), amount);

emit BridgingAccepted(address(receiver), address(token), amount);
return true;
}
}
6 changes: 3 additions & 3 deletions ethereum/src/ERC20Treasury.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {IERC20Treasury, WithdrawMessage} from "./interfaces/IERC20Treasury.sol";
import {GRC_20_GATEWAY_ADDRESS} from "./libraries/Environment.sol";
import {IERC20Treasury} from "./interfaces/IERC20Treasury.sol";
import {VFT_GATEWAY_ADDRESS} from "./libraries/Environment.sol";
import {IMessageQueue, IMessageQueueReceiver, VaraMessage} from "./interfaces/IMessageQueue.sol";

contract ERC20Treasury is IERC20Treasury, Context, IMessageQueueReceiver {
Expand Down Expand Up @@ -55,7 +55,7 @@ contract ERC20Treasury is IERC20Treasury, Context, IMessageQueueReceiver {
if (vara_msg.receiver != address(this)) {
revert BadEthAddress();
}
if (vara_msg.sender != GRC_20_GATEWAY_ADDRESS) {
if (vara_msg.sender != VFT_GATEWAY_ADDRESS) {
revert BadVaraAddress();
}

Expand Down
17 changes: 17 additions & 0 deletions ethereum/src/ERC20VaraSupply.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pragma solidity ^0.8.24;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract ERC20VaraSupply is ERC20Burnable, Ownable {
constructor(
address owner,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol) Ownable(owner) {}

function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
35 changes: 35 additions & 0 deletions ethereum/src/interfaces/IERC20Gateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.8.24;

struct BridgingRequest {
address receiver;
address token;
uint128 amount;
}

interface IERC20Gateway {
error NotAuthorized();
error BadArguments();
error BadEthAddress();
error BadVaraAddress();

event BridgingRequested(
address indexed from,
bytes32 indexed to,
address indexed token,
uint256 amount
);
event BridgingAccepted(
address indexed to,
address indexed token,
uint256 amount
);
}

library Packer {
function pack(
BridgingRequest calldata message
) external pure returns (bytes memory) {
return
abi.encodePacked(message.receiver, message.token, message.amount);
}
}
6 changes: 5 additions & 1 deletion ethereum/src/libraries/Environment.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
pragma solidity ^0.8.24;

// ALICE
bytes32 constant VFT_GATEWAY_ADDRESS = bytes32(
0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
);

// ALICE
bytes32 constant GRC_20_GATEWAY_ADDRESS = bytes32(
bytes32 constant VFT_TREASURY_ADDRESS = bytes32(
0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
);
Loading
Loading