Skip to content

Commit

Permalink
Upgrade the bridge to work with chain signatures instead of light cli…
Browse files Browse the repository at this point in the history
…ent proofs
  • Loading branch information
kiseln committed Aug 22, 2024
1 parent b600af2 commit d7fce8e
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 184 deletions.
36 changes: 36 additions & 0 deletions erc20-bridge-token/contracts/Borsh.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

library Borsh {
function encodeUint32(uint32 val) internal pure returns (bytes memory) {
return abi.encodePacked(swapBytes4(val));
}

function encodeUint128(uint128 val) internal pure returns (bytes memory) {
return abi.encodePacked(swapBytes16(val));
}

function encodeString(string memory val) internal pure returns (bytes memory) {
bytes memory b = bytes(val);
return bytes.concat(
encodeUint32(uint32(b.length)),
bytes(val)
);
}

function encodeAddress(address val) internal pure returns (bytes20) {
return bytes20(val);
}

function swapBytes4(uint32 v) internal pure returns (uint32) {
v = ((v & 0x00ff00ff) << 8) | ((v & 0xff00ff00) >> 8);
return (v << 16) | (v >> 16);
}

function swapBytes16(uint128 v) internal pure returns (uint128) {
v = ((v & 0x00ff00ff00ff00ff00ff00ff00ff00ff) << 8) | ((v & 0xff00ff00ff00ff00ff00ff00ff00ff00) >> 8);
v = ((v & 0x0000ffff0000ffff0000ffff0000ffff) << 16) | ((v & 0xffff0000ffff0000ffff0000ffff0000) >> 16);
v = ((v & 0x00000000ffffffff00000000ffffffff) << 32) | ((v & 0xffffffff00000000ffffffff00000000) >> 32);
return (v << 64) | (v >> 64);
}
}
108 changes: 60 additions & 48 deletions erc20-bridge-token/contracts/BridgeTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ pragma solidity ^0.8.24;
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "rainbow-bridge-sol/nearprover/contracts/INearProver.sol";
import "rainbow-bridge-sol/nearprover/contracts/ProofDecoder.sol";

import "./ProofConsumer.sol";
import "./BridgeToken.sol";
import "./ResultsDecoder.sol";
import "./SelectivePausableUpgradable.sol";
import "./Borsh.sol";

contract BridgeTokenFactory is
ProofConsumer,
UUPSUpgradeable,
AccessControlUpgradeable,
SelectivePausableUpgradable
Expand All @@ -35,12 +31,28 @@ contract BridgeTokenFactory is
bool private _isWhitelistModeEnabled;

address public tokenImplementationAddress;
address public nearBridgeDerivedAddress;

bytes32 public constant PAUSABLE_ADMIN_ROLE = keccak256("PAUSABLE_ADMIN_ROLE");
uint constant UNPAUSED_ALL = 0;
uint constant PAUSED_WITHDRAW = 1 << 0;
uint constant PAUSED_DEPOSIT = 1 << 1;

struct BridgeDeposit {
uint128 nonce;
string token;
uint128 amount;
address recipient;
address relayer;
}

struct MetadataPayload {
string token;
string name;
string symbol;
uint8 decimals;
}

// Event when funds are withdrawn from Ethereum back to NEAR.
event Withdraw(
string token,
Expand All @@ -60,21 +72,16 @@ contract BridgeTokenFactory is
uint8 decimals
);

error InvalidSignature();

// BridgeTokenFactory is linked to the bridge token factory on NEAR side.
// It also links to the prover that it uses to unlock the tokens.
function initialize(
address _tokenImplementationAddress,
bytes memory _nearTokenLocker,
INearProver _prover,
uint64 _minBlockAcceptanceHeight
address _nearBridgeDerivedAddress
) external initializer {
require(_nearTokenLocker.length > 0, "Invalid Near Token Locker address");
require(address(_prover) != address(0), "Invalid Near prover address");

nearTokenLocker = _nearTokenLocker;
prover = _prover;
minBlockAcceptanceHeight = _minBlockAcceptanceHeight;
tokenImplementationAddress = _tokenImplementationAddress;
nearBridgeDerivedAddress = _nearBridgeDerivedAddress;

__UUPSUpgradeable_init();
__AccessControl_init();
Expand All @@ -97,43 +104,44 @@ contract BridgeTokenFactory is
return _nearToEthToken[nearTokenId];
}

function newBridgeToken(
bytes memory proofData,
uint64 proofBlockHeight
) external returns (address) {
ProofDecoder.ExecutionStatus memory status = _parseAndConsumeProof(
proofData,
proofBlockHeight
);
ResultsDecoder.MetadataResult memory result = ResultsDecoder.decodeMetadataResult(
status.successValue
function newBridgeToken(bytes calldata signatureData, MetadataPayload calldata metadata) external returns (address) {
bytes memory borshEncoded = abi.encode(
Borsh.encodeString(metadata.token),
Borsh.encodeString(metadata.name),
Borsh.encodeString(metadata.symbol),
metadata.decimals
);
bytes32 hashed = keccak256(borshEncoded);

if (ECDSA.recover(hashed, signatureData) != nearBridgeDerivedAddress) {
revert InvalidSignature();
}

require(!_isBridgeToken[_nearToEthToken[result.token]], "ERR_TOKEN_EXIST");
require(!_isBridgeToken[_nearToEthToken[metadata.token]], "ERR_TOKEN_EXIST");

address bridgeTokenProxy = address(
new ERC1967Proxy(
tokenImplementationAddress,
abi.encodeWithSelector(
BridgeToken.initialize.selector,
result.name,
result.symbol,
result.decimals
metadata.name,
metadata.symbol,
metadata.decimals
)
)
);

emit SetMetadata(
bridgeTokenProxy,
result.token,
result.name,
result.symbol,
result.decimals
metadata.token,
metadata.name,
metadata.symbol,
metadata.decimals
);

_isBridgeToken[address(bridgeTokenProxy)] = true;
_ethToNearToken[address(bridgeTokenProxy)] = result.token;
_nearToEthToken[result.token] = address(bridgeTokenProxy);
_ethToNearToken[address(bridgeTokenProxy)] = metadata.token;
_nearToEthToken[metadata.token] = address(bridgeTokenProxy);

return bridgeTokenProxy;
}
Expand All @@ -158,22 +166,26 @@ contract BridgeTokenFactory is
);
}

function deposit(
bytes memory proofData,
uint64 proofBlockHeight
) external whenNotPaused(PAUSED_DEPOSIT) {
ProofDecoder.ExecutionStatus memory status = _parseAndConsumeProof(
proofData,
proofBlockHeight
);
ResultsDecoder.LockResult memory result = ResultsDecoder.decodeLockResult(
status.successValue
function deposit(bytes calldata signatureData, BridgeDeposit calldata bridgeDeposit) external whenNotPaused(PAUSED_DEPOSIT) {
bytes memory borshEncoded = abi.encode(
Borsh.encodeUint128(bridgeDeposit.nonce),
Borsh.encodeString(bridgeDeposit.token),
Borsh.encodeUint128(bridgeDeposit.amount),
0x00, // variant 1 in rust enum
Borsh.encodeAddress(bridgeDeposit.recipient),
bridgeDeposit.relayer == address(0) ? 0x00 : 0x01, // None or Some in rust
Borsh.encodeAddress(bridgeDeposit.relayer)
);
bytes32 hashed = keccak256(borshEncoded);

if (ECDSA.recover(hashed, signatureData) != nearBridgeDerivedAddress) {
revert InvalidSignature();
}

require(_isBridgeToken[_nearToEthToken[result.token]], "ERR_NOT_BRIDGE_TOKEN");
BridgeToken(_nearToEthToken[result.token]).mint(result.recipient, result.amount);
require(_isBridgeToken[_nearToEthToken[bridgeDeposit.token]], "ERR_NOT_BRIDGE_TOKEN");
BridgeToken(_nearToEthToken[bridgeDeposit.token]).mint(bridgeDeposit.recipient, bridgeDeposit.amount);

emit Deposit(result.token, result.amount, result.recipient);
emit Deposit(bridgeDeposit.token, bridgeDeposit.amount, bridgeDeposit.recipient);
}

function withdraw(
Expand Down
72 changes: 0 additions & 72 deletions erc20-bridge-token/contracts/ProofConsumer.sol

This file was deleted.

51 changes: 0 additions & 51 deletions erc20-bridge-token/contracts/ResultsDecoder.sol

This file was deleted.

13 changes: 0 additions & 13 deletions erc20-bridge-token/contracts/test/NearProverMock.sol

This file was deleted.

0 comments on commit d7fce8e

Please sign in to comment.