Skip to content
This repository was archived by the owner on May 9, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
518a2d6
Initial commit
dankostiuk Sep 3, 2021
1ad7840
Merge branch 'release/v1.0.0'
dankostiuk Sep 3, 2021
d83d72f
update readme
dankostiuk Sep 3, 2021
23cfe82
indexing proposalExecution event
dankostiuk Sep 3, 2021
e2dcda8
adjusted fields on bridge's ProposalExecution event
dankostiuk Sep 7, 2021
a390061
pass in deconstructed fields to ProposalExecution event
dankostiuk Sep 7, 2021
1c817be
moved ERC20ProposalExecuted to ERC20Handler
dankostiuk Sep 7, 2021
c498d12
added graph files
dankostiuk Oct 16, 2021
3385e5f
added commands to deploy to temp graph url
dankostiuk Oct 19, 2021
0bc7689
update to more recent startBlock
dankostiuk Oct 19, 2021
0c1aa45
updated erc20handler contract entity
dankostiuk Oct 20, 2021
6b0232e
fix timestamp field for subgraph schema
dankostiuk Oct 20, 2021
57756e0
update graph url + subgraph config
dankostiuk Oct 22, 2021
6466ad5
deploying to toronto
dankostiuk Nov 24, 2021
5b29e60
add extra fields to support pending transfer events
dankostiuk Dec 15, 2021
443be43
remove direction
dankostiuk Dec 15, 2021
1d292fa
updated contract address
dankostiuk Dec 15, 2021
097a603
add SXVault.sol and tasks
dankostiuk Jan 25, 2022
3989ce2
Test removing requirement of datahash in executeProposal
dankostiuk Jan 26, 2022
7d5879f
Added ERC20SXHandler to deal with native sx
dankostiuk Jan 26, 2022
c1598dc
update contract name for erc20SXHandler
dankostiuk Jan 26, 2022
a66ac7d
calling SXVault contract using interface
dankostiuk Jan 26, 2022
4c404a9
remove comment
dankostiuk Jan 26, 2022
6d5e418
remove comments
dankostiuk Jan 26, 2022
3e5fdd3
additional changes
dankostiuk Jan 27, 2022
19d7c48
added tasks and updating ipfs url
dankostiuk Feb 8, 2022
8027491
updated graph ipfs url again
dankostiuk Feb 8, 2022
2d277cb
Now can burn erc20 tokens that don't implement burnFrom
dankostiuk Feb 16, 2022
2b21a57
Prevent SX deposits out of SXN
dankostiuk Feb 22, 2022
78207d4
use erc20safe.lockerc20 to call safeTransferFrom
dankostiuk Feb 22, 2022
1c42b1e
Apply formatting for better readability + invoke manualBurnERC20 inst…
dankostiuk Mar 3, 2022
8654f56
Include formatting on remaining sol files
dankostiuk Mar 3, 2022
083bae9
Further formatting changes
dankostiuk Mar 3, 2022
34316a7
remove fundERC20 function, add SPDX license identifiers
dankostiuk Mar 8, 2022
3978b43
add husky
akshar Mar 8, 2022
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
17 changes: 0 additions & 17 deletions .github/pull_request_template.md

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.env
.idea/
/build
/artifacts
/cache
.history/
node_modules/
gethdata/
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# chainbridge-solidity

## SX Network Info

This repo is based off `v1.0.0` of upstream repo https://github.com/ChainSafe/chainbridge-solidity.git.

It is referenced by https://gitlab.com/nextgenbt/sx-network/chainbridge-deploy which maintains our version of the cb-sol-cli tool.

Updates to ChainBridge-related contracts should be merged into the `master` branch here prior to cloning/pulling from the repo above.

## ChainBridge

[![Coverage Status](https://coveralls.io/repos/github/ChainSafe/chainbridge-solidity/badge.svg?branch=master)](https://coveralls.io/github/ChainSafe/chainbridge-solidity?branch=master)

ChainBridge uses Solidity smart contracts to enable transfers to and from EVM compatible chains. These contracts consist of a core bridge contract (Bridge.sol) and a set of handler contracts (ERC20Handler.sol, ERC721Handler.sol, and GenericHandler.sol). The bridge contract is responsible for initiating, voting on, and executing proposed transfers. The handlers are used by the bridge contract to interact with other existing contracts.
Expand Down Expand Up @@ -29,5 +39,3 @@ Requires `nodejs` and `npm`.
`make test`: Runs truffle tests.

`make compile`: Compile contracts.


259 changes: 138 additions & 121 deletions contracts/handlers/ERC20Handler.sol
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
pragma solidity 0.6.4;
pragma experimental ABIEncoderV2;

import "../interfaces/IDepositExecute.sol";
import "./HandlerHelpers.sol";
import "../ERC20Safe.sol";
import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol";
import '../interfaces/IDepositExecute.sol';
import './HandlerHelpers.sol';
import '../ERC20Safe.sol';
import '@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol';

/**
@title Handles ERC20 deposits and deposit executions.
@author ChainSafe Systems.
@notice This contract is intended to be used with the Bridge contract.
*/
contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
struct DepositRecord {
address _tokenAddress;
uint8 _lenDestinationRecipientAddress;
uint8 _destinationChainID;
bytes32 _resourceID;
bytes _destinationRecipientAddress;
address _depositer;
uint _amount;
}
struct DepositRecord {
address _tokenAddress;
uint8 _lenDestinationRecipientAddress;
uint8 _destinationChainID;
bytes32 _resourceID;
bytes _destinationRecipientAddress;
address _depositer;
uint256 _amount;
}

// depositNonce => Deposit Record
mapping(uint8 => mapping(uint64 => DepositRecord)) public _depositRecords;

event ERC20InitiateBridge(address indexed recipientAddress, bytes32 indexed resourceID, uint256 amount);

// depositNonce => Deposit Record
mapping (uint8 => mapping(uint64 => DepositRecord)) public _depositRecords;
event ERC20ProposalExecuted(address indexed recipientAddress, bytes32 indexed resourceID, uint256 amount);

/**
/**
@param bridgeAddress Contract address of previously deployed Bridge.
@param initialResourceIDs Resource IDs are used to identify a specific contract address.
These are the Resource IDs this contract will initially support.
Expand All @@ -37,27 +41,26 @@ contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
@dev {initialResourceIDs} and {initialContractAddresses} must have the same length (one resourceID for every address).
Also, these arrays must be ordered in the way that {initialResourceIDs}[0] is the intended resourceID for {initialContractAddresses}[0].
*/
constructor(
address bridgeAddress,
bytes32[] memory initialResourceIDs,
address[] memory initialContractAddresses,
address[] memory burnableContractAddresses
) public {
require(initialResourceIDs.length == initialContractAddresses.length,
"initialResourceIDs and initialContractAddresses len mismatch");

_bridgeAddress = bridgeAddress;

for (uint256 i = 0; i < initialResourceIDs.length; i++) {
_setResource(initialResourceIDs[i], initialContractAddresses[i]);
}

for (uint256 i = 0; i < burnableContractAddresses.length; i++) {
_setBurnable(burnableContractAddresses[i]);
}
constructor(
address bridgeAddress,
bytes32[] memory initialResourceIDs,
address[] memory initialContractAddresses,
address[] memory burnableContractAddresses
) public {
require(initialResourceIDs.length == initialContractAddresses.length, 'initialResourceIDs and initialContractAddresses len mismatch');

_bridgeAddress = bridgeAddress;

for (uint256 i = 0; i < initialResourceIDs.length; i++) {
_setResource(initialResourceIDs[i], initialContractAddresses[i]);
}

for (uint256 i = 0; i < burnableContractAddresses.length; i++) {
_setBurnable(burnableContractAddresses[i]);
}
}

/**
/**
@param depositNonce This ID will have been generated by the Bridge contract.
@param destId ID of chain deposit will be bridged to.
@return DepositRecord which consists of:
Expand All @@ -69,11 +72,11 @@ contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
- _depositer Address that initially called {deposit} in the Bridge contract.
- _amount Amount of tokens that were deposited.
*/
function getDepositRecord(uint64 depositNonce, uint8 destId) external view returns (DepositRecord memory) {
return _depositRecords[destId][depositNonce];
}
function getDepositRecord(uint64 depositNonce, uint8 destId) external view returns (DepositRecord memory) {
return _depositRecords[destId][depositNonce];
}

/**
/**
@notice A deposit is initiatied by making a deposit in the Bridge contract.
@param destinationChainID Chain ID of chain tokens are expected to be bridged to.
@param depositNonce This value is generated as an ID by the Bridge contract.
Expand All @@ -86,54 +89,61 @@ contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
recipientAddress bytes bytes 64 - END
@dev Depending if the corresponding {tokenAddress} for the parsed {resourceID} is
marked true in {_burnList}, deposited tokens will be burned, if not, they will be locked.
@notice Emits {ERC20InitiateBridge} event with recipientAddress, resourceId, and amount.
*/
function deposit(
bytes32 resourceID,
uint8 destinationChainID,
uint64 depositNonce,
address depositer,
bytes calldata data
) external override onlyBridge {
bytes memory recipientAddress;
uint256 amount;
uint256 lenRecipientAddress;

assembly {

amount := calldataload(0xC4)

recipientAddress := mload(0x40)
lenRecipientAddress := calldataload(0xE4)
mstore(0x40, add(0x20, add(recipientAddress, lenRecipientAddress)))

calldatacopy(
recipientAddress, // copy to destinationRecipientAddress
0xE4, // copy from calldata @ 0x104
sub(calldatasize(), 0xE) // copy size (calldatasize - 0x104)
)
}

address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");

if (_burnList[tokenAddress]) {
burnERC20(tokenAddress, depositer, amount);
} else {
lockERC20(tokenAddress, depositer, address(this), amount);
}

_depositRecords[destinationChainID][depositNonce] = DepositRecord(
tokenAddress,
uint8(lenRecipientAddress),
destinationChainID,
resourceID,
recipientAddress,
depositer,
amount
);
function deposit(
bytes32 resourceID,
uint8 destinationChainID,
uint64 depositNonce,
address depositer,
bytes calldata data
) external override onlyBridge {
bytes memory recipientAddress;
uint256 amount;
uint256 lenRecipientAddress;

assembly {
amount := calldataload(0xC4)

recipientAddress := mload(0x40)
lenRecipientAddress := calldataload(0xE4)
mstore(0x40, add(0x20, add(recipientAddress, lenRecipientAddress)))

calldatacopy(
recipientAddress, // copy to destinationRecipientAddress
0xE4, // copy from calldata @ 0x104
sub(calldatasize(), 0xE) // copy size (calldatasize - 0x104)
)
}

/**
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];
require(_contractWhitelist[tokenAddress], 'provided tokenAddress is not whitelisted');

if (_burnList[tokenAddress]) {
// effectively 'burn' ERC20 that does not implement ERC20Burnable 'burnFrom()'
lockERC20(tokenAddress, depositer, address(0x000000000000000000000000000000000000dEaD), amount);
} else {
lockERC20(tokenAddress, depositer, address(this), amount);
}

_depositRecords[destinationChainID][depositNonce] = DepositRecord(
tokenAddress,
uint8(lenRecipientAddress),
destinationChainID,
resourceID,
recipientAddress,
depositer,
amount
);

bytes20 destinationRecipientAddress;
assembly {
destinationRecipientAddress := mload(add(recipientAddress, 0x20))
}
emit ERC20InitiateBridge(address(destinationRecipientAddress), resourceID, 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 data Consists of {resourceID}, {amount}, {lenDestinationRecipientAddress},
Expand All @@ -142,49 +152,56 @@ contract ERC20Handler is IDepositExecute, HandlerHelpers, ERC20Safe {
amount uint256 bytes 0 - 32
destinationRecipientAddress length uint256 bytes 32 - 64
destinationRecipientAddress bytes bytes 64 - END
@notice Emits {ERC20ProposalExecuted} event with recipientAddress, resourceId, and amount.
*/
function executeProposal(bytes32 resourceID, bytes calldata data) external override onlyBridge {
uint256 amount;
bytes memory destinationRecipientAddress;

assembly {
amount := calldataload(0x64)

destinationRecipientAddress := mload(0x40)
let lenDestinationRecipientAddress := calldataload(0x84)
mstore(0x40, add(0x20, add(destinationRecipientAddress, lenDestinationRecipientAddress)))

// in the calldata the destinationRecipientAddress is stored at 0xC4 after accounting for the function signature and length declaration
calldatacopy(
destinationRecipientAddress, // copy to destinationRecipientAddress
0x84, // copy from calldata @ 0x84
sub(calldatasize(), 0x84) // copy size to the end of calldata
)
}

bytes20 recipientAddress;
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];

assembly {
recipientAddress := mload(add(destinationRecipientAddress, 0x20))
}

require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted");

if (_burnList[tokenAddress]) {
mintERC20(tokenAddress, address(recipientAddress), amount);
} else {
releaseERC20(tokenAddress, address(recipientAddress), amount);
}
function executeProposal(bytes32 resourceID, bytes calldata data) external override onlyBridge {
uint256 amount;
bytes memory destinationRecipientAddress;

assembly {
amount := calldataload(0x64)

destinationRecipientAddress := mload(0x40)
let lenDestinationRecipientAddress := calldataload(0x84)
mstore(0x40, add(0x20, add(destinationRecipientAddress, lenDestinationRecipientAddress)))

// in the calldata the destinationRecipientAddress is stored at 0xC4 after accounting for the function signature and length declaration
calldatacopy(
destinationRecipientAddress, // copy to destinationRecipientAddress
0x84, // copy from calldata @ 0x84
sub(calldatasize(), 0x84) // copy size to the end of calldata
)
}

/**
bytes20 recipientAddress;
address tokenAddress = _resourceIDToTokenContractAddress[resourceID];

assembly {
recipientAddress := mload(add(destinationRecipientAddress, 0x20))
}

require(_contractWhitelist[tokenAddress], 'provided tokenAddress is not whitelisted');

if (_burnList[tokenAddress]) {
mintERC20(tokenAddress, address(recipientAddress), amount);
} else {
releaseERC20(tokenAddress, address(recipientAddress), amount);
}

emit ERC20ProposalExecuted(address(recipientAddress), resourceID, amount);
}

/**
@notice Used to manually release ERC20 tokens from ERC20Safe.
@param tokenAddress Address of token contract to release.
@param recipient Address to release tokens to.
@param amount The amount of ERC20 tokens to release.
*/
function withdraw(address tokenAddress, address recipient, uint amount) external override onlyBridge {
releaseERC20(tokenAddress, recipient, amount);
}
function withdraw(
address tokenAddress,
address recipient,
uint256 amount
) external override onlyBridge {
releaseERC20(tokenAddress, recipient, amount);
}
}
Loading