Skip to content

Commit

Permalink
ZIL-5483: Token bridge implementation (#328)
Browse files Browse the repository at this point in the history
* Token bridge implementation

* Add separate relay with metadata

* Add changes in response to feedback
  • Loading branch information
WuBruno authored Dec 20, 2023
1 parent 31918a2 commit 958410c
Show file tree
Hide file tree
Showing 10 changed files with 459 additions and 293 deletions.
11 changes: 7 additions & 4 deletions products/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,23 +238,26 @@ The MVP bridge will run on a gossip network with validators managed by Zilliqa
- [ ] Update periphery contracts
- [ ] Split bridged incoming and outgoing interfaces
- [ ] Support response instead of fire&forget
- [ ] Update foundry tests
- [x] Update foundry tests
- [ ] Update hardhat tests
- [ ] Synchronizing validators cross-chain
- [ ] Update validator set
- [-] Fuzz + invariant testing
- [ ] Write deployment scripts
- [ ] Support CREATE2
- [ ] [Deterministic Deployment Proxy](https://github.com/Arachnid/deterministic-deployment-proxy)
- [ ] Track last event block number for light client event censorship resistance
- [ ] **Off-Chain Validator Nodes** & **Validator Node Lib**
- Binary and lib would be developed together. Lib will be refactored out later to be used for ZQ2
- [ ] Determine type of connection to use to connect to non-zilliqa chains
- Light node/client? [helios](https://github.com/a16z/helios)
- [ ] P2P network for sharing signatures - [rust-libp2p](https://github.com/libp2p/rust-libp2p)
- [ ] Read & Write to chains
- [ ] Validator slashing
- [ ] Zilliqa maintained full nodes of non-zq chain. UCCB validators use light nodes on it
- [ ] Integrate into ZQ2 consensus network to share signatures
- [ ] Periodic gas refund for validators
- [ ] Light client implementation
- [ ] Determine type of connection to use to connect to non-zilliqa chains
- [ ] Zilliqa maintained full nodes of non-zq chain. UCCB validators use light nodes on it
- [ ] Check logsBloom for potential events in the block
- [ ] Retrieve block receipts, check for logs and verify receipt root
- [ ] **Frontend DApp**
- [ ] Boilerplate setup (Vite React with Rainbow Kit)
306 changes: 153 additions & 153 deletions products/bridge/smart-contracts/contracts/Test.sol
Original file line number Diff line number Diff line change
@@ -1,153 +1,153 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "contracts/periphery/Bridged.sol";
import "contracts/core/ChainGateway.sol";

contract Twin is Initializable, Bridged, BridgedTwin {
function initialize(
ChainGateway _relayer,
uint _twinChainId
) public initializer {
__Bridged_init(_relayer);
__BridgedTwin_init(_twinChainId);
}

function start(address target, uint num) external {
uint nonce = relay(
twinChainId,
target,
abi.encodeWithSignature("test(uint256)", num),
1_000_000
);
}

event Succeeded(uint);
event Failed(string);

function finish(
bool success,
bytes calldata res,
uint nonce
) external onlyRelayer {
if (success) {
uint num = abi.decode(res, (uint));
emit Succeeded(num);
} else {
bytes4 sig = bytes4(res[:4]);
bytes memory err = bytes(res[4:]);
emit Failed(abi.decode(err, (string)));
}
}

function startSum(address target, uint num) external {
relay(
twinChainId,
target,
abi.encodeWithSignature("testSum(uint256)", num),
1_000_000
);
}

function finishSum(
bool success,
bytes calldata res,
uint nonce
) external onlyRelayer {
if (success) {
uint num = abi.decode(res, (uint));
emit Succeeded(num);
} else {
bytes4(res[:4]);
bytes memory err = bytes(res[4:]);
emit Failed(abi.decode(err, (string)));
}
}

function startNoReturn(address target, uint num) external {
relay(
twinChainId,
target,
abi.encodeWithSignature("testNoReturn(uint256)", num),
1_000_000
);
}

event SucceededNoReturn();

function finishNoReturn(
bool success,
bytes calldata res,
uint nonce
) external onlyRelayer {
if (success) {
emit SucceededNoReturn();
} else {
bytes4(res[:4]);
bytes memory err = bytes(res[4:]);
emit Failed(abi.decode(err, (string)));
}
}

function startMultipleReturn(address target, uint num) external {
relay(
twinChainId,
target,
abi.encodeWithSignature("testMultipleReturn(uint256)", num),
1_000_000
);
}

event SucceededMultipleReturn(uint, uint, uint);

function finishMultipleReturn(
bool success,
bytes calldata res,
uint nonce
) external onlyRelayer {
if (success) {
(uint num, uint num2, uint num3) = abi.decode(
res,
(uint, uint, uint)
);
emit SucceededMultipleReturn(num, num2, num3);
} else {
bytes4(res[:4]);
bytes memory err = bytes(res[4:]);
emit Failed(abi.decode(err, (string)));
}
}
}

contract Target {
uint private _num = 1;

event TestNoReturn(uint num);
event TestSum(uint num);

function test(uint num_) external pure returns (uint) {
require(num_ < 1000, "Too large");
return num_ + 1;
}

function testSum(uint num_) external returns (uint) {
_num += num_;
emit TestSum(_num);
return _num;
}

function num() external view returns (uint) {
return _num;
}

function testNoReturn(uint num_) external {
emit TestNoReturn(num_ + 1);
}

function testMultipleReturn(
uint num_
) external pure returns (uint, uint, uint) {
return (num_ + 1, num_ + 2, num_ + 3);
}
}
// // SPDX-License-Identifier: UNLICENSED
// pragma solidity ^0.8.20;

// import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
// import "contracts/periphery/Bridged.sol";
// import "contracts/core/ChainGateway.sol";

// contract Twin is Initializable, Bridged, BridgedTwin {
// function initialize(
// ChainGateway _relayer,
// uint _twinChainId
// ) public initializer {
// __Bridged_init(_relayer);
// __BridgedTwin_init(_twinChainId);
// }

// function start(address target, uint num) external {
// uint nonce = relay(
// twinChainId,
// target,
// abi.encodeWithSignature("test(uint256)", num),
// 1_000_000
// );
// }

// event Succeeded(uint);
// event Failed(string);

// function finish(
// bool success,
// bytes calldata res,
// uint nonce
// ) external onlyRelayer {
// if (success) {
// uint num = abi.decode(res, (uint));
// emit Succeeded(num);
// } else {
// bytes4 sig = bytes4(res[:4]);
// bytes memory err = bytes(res[4:]);
// emit Failed(abi.decode(err, (string)));
// }
// }

// function startSum(address target, uint num) external {
// relay(
// twinChainId,
// target,
// abi.encodeWithSignature("testSum(uint256)", num),
// 1_000_000
// );
// }

// function finishSum(
// bool success,
// bytes calldata res,
// uint nonce
// ) external onlyRelayer {
// if (success) {
// uint num = abi.decode(res, (uint));
// emit Succeeded(num);
// } else {
// bytes4(res[:4]);
// bytes memory err = bytes(res[4:]);
// emit Failed(abi.decode(err, (string)));
// }
// }

// function startNoReturn(address target, uint num) external {
// relay(
// twinChainId,
// target,
// abi.encodeWithSignature("testNoReturn(uint256)", num),
// 1_000_000
// );
// }

// event SucceededNoReturn();

// function finishNoReturn(
// bool success,
// bytes calldata res,
// uint nonce
// ) external onlyRelayer {
// if (success) {
// emit SucceededNoReturn();
// } else {
// bytes4(res[:4]);
// bytes memory err = bytes(res[4:]);
// emit Failed(abi.decode(err, (string)));
// }
// }

// function startMultipleReturn(address target, uint num) external {
// relay(
// twinChainId,
// target,
// abi.encodeWithSignature("testMultipleReturn(uint256)", num),
// 1_000_000
// );
// }

// event SucceededMultipleReturn(uint, uint, uint);

// function finishMultipleReturn(
// bool success,
// bytes calldata res,
// uint nonce
// ) external onlyRelayer {
// if (success) {
// (uint num, uint num2, uint num3) = abi.decode(
// res,
// (uint, uint, uint)
// );
// emit SucceededMultipleReturn(num, num2, num3);
// } else {
// bytes4(res[:4]);
// bytes memory err = bytes(res[4:]);
// emit Failed(abi.decode(err, (string)));
// }
// }
// }

// contract Target {
// uint private _num = 1;

// event TestNoReturn(uint num);
// event TestSum(uint num);

// function test(uint num_) external pure returns (uint) {
// require(num_ < 1000, "Too large");
// return num_ + 1;
// }

// function testSum(uint num_) external returns (uint) {
// _num += num_;
// emit TestSum(_num);
// return _num;
// }

// function num() external view returns (uint) {
// return _num;
// }

// function testNoReturn(uint num_) external {
// emit TestNoReturn(num_ + 1);
// }

// function testMultipleReturn(
// uint num_
// ) external pure returns (uint, uint, uint) {
// return (num_ + 1, num_ + 2, num_ + 3);
// }
// }
37 changes: 29 additions & 8 deletions products/bridge/smart-contracts/contracts/core/Relayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,48 @@ interface IRelayerEvents {
);
}

interface IRelayer is IRelayerEvents {
function nonce() external returns (uint);
interface IRelayer is IRelayerEvents {}

function relay(
uint targetChainId,
address target,
bytes calldata call,
uint gasLimit
) external returns (uint);
struct CallMetadata {
uint sourceChainId;
address sender;
}

contract Relayer is IRelayer {
uint public nonce;

// Use this function to relay a call with metadata. This is useful for calling surrogate contracts.
// Ensure the surrogate implements this interface
function relayWithMetadata(
uint targetChainId,
address target,
bytes4 callSelector,
bytes calldata callData,
uint gasLimit
) external returns (uint) {
emit Relayed(
targetChainId,
target,
abi.encodeWithSelector(
callSelector,
abi.encode(CallMetadata(block.chainid, msg.sender)),
callData
),
gasLimit,
nonce
);

return nonce++;
}

function relay(
uint targetChainId,
address target,
bytes calldata call,
uint gasLimit
) external returns (uint) {
emit Relayed(targetChainId, target, call, gasLimit, nonce);

return nonce++;
}
}
Loading

0 comments on commit 958410c

Please sign in to comment.