Add mediators contracts (#6)
* Add mintable burnable kitty contract * Add mediators contracts * Update flatten script * Add mediator deploy scripts * Add poa-bridge-contracts submodule
This commit is contained in:
parent
af0bcb6196
commit
138dd3932c
11
.env.example
11
.env.example
|
@ -1,8 +1,19 @@
|
|||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=67..14
|
||||
DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
|
||||
|
||||
HOME_RPC_URL=https://sokol.poa.network
|
||||
HOME_DEPLOYMENT_GAS_PRICE=1000000000
|
||||
HOME_AMB_BRIDGE=0x0000000000000000000000000000000000000000
|
||||
HOME_MEDIATOR_REQUEST_GAS_LIMIT=1000000
|
||||
HOME_MEDIATOR_OWNER=0x0000000000000000000000000000000000000000
|
||||
HOME_UPGRADEABLE_ADMIN=0x0000000000000000000000000000000000000000
|
||||
|
||||
FOREIGN_RPC_URL=https://sokol.poa.network
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=1000000000
|
||||
FOREIGN_AMB_BRIDGE=0x0000000000000000000000000000000000000000
|
||||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=1000000
|
||||
FOREIGN_MEDIATOR_OWNER=0x0000000000000000000000000000000000000000
|
||||
FOREIGN_UPGRADEABLE_ADMIN=0x0000000000000000000000000000000000000000
|
||||
|
||||
CRYPTOKITTIES_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
KITTIES_AMOUNT=1
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
bridge-contracts
|
11
.eslintrc
11
.eslintrc
|
@ -1,12 +1,21 @@
|
|||
{
|
||||
"extends": ["plugin:node/recommended", "airbnb-base", "plugin:prettier/recommended"],
|
||||
"plugins": ["node"],
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"artifacts": false,
|
||||
"contract": false,
|
||||
"web3": false
|
||||
},
|
||||
"rules": {
|
||||
"no-await-in-loop": "off",
|
||||
"consistent-return": "off",
|
||||
"no-console": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-use-before-define": ["error", { "functions": false }],
|
||||
"node/no-unpublished-require": "off"
|
||||
"node/no-unpublished-require": "off",
|
||||
"func-names": "off"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "bridge-contracts"]
|
||||
path = bridge-contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
|
@ -8,6 +8,7 @@
|
|||
"not-rely-on-time": "off",
|
||||
"const-name-snakecase": "off",
|
||||
"no-inline-assembly": "off",
|
||||
"multiple-sends": "off",
|
||||
"compiler-version": ["error", "0.4.24"]
|
||||
}
|
||||
}
|
||||
|
|
33
README.md
33
README.md
|
@ -1,5 +1,10 @@
|
|||
# cryptokitties-xdai-demo
|
||||
|
||||
#### Clone the repository
|
||||
```bash
|
||||
git clone --recursive https://github.com/poanetwork/cryptokitties-xdai-demo.git
|
||||
```
|
||||
|
||||
#### Install dependencies
|
||||
```bash
|
||||
yarn
|
||||
|
@ -22,10 +27,32 @@ DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
|
|||
HOME_RPC_URL=https://sokol.poa.network
|
||||
# The "gasPrice" parameter set in every deployment/configuration transaction on Home network (in Wei).
|
||||
HOME_DEPLOYMENT_GAS_PRICE=1000000000
|
||||
# The address of the existing AMB bridge in the Home network that will be used to pass messages
|
||||
# to the Foreign network.
|
||||
HOME_AMB_BRIDGE=0x0000000000000000000000000000000000000000
|
||||
# The gas limit that will be used in the execution of the message passed to the mediator contract
|
||||
# in the Foreign network.
|
||||
HOME_MEDIATOR_REQUEST_GAS_LIMIT=1000000
|
||||
# Address on Home network with permissions to change parameters of the mediator contract.
|
||||
HOME_MEDIATOR_OWNER=0x0000000000000000000000000000000000000000
|
||||
# Address on Home network with permissions to upgrade the mediator contract
|
||||
HOME_UPGRADEABLE_ADMIN=0x0000000000000000000000000000000000000000
|
||||
|
||||
# The RPC channel to a Foreign node able to handle deployment/configuration transactions.
|
||||
FOREIGN_RPC_URL=https://sokol.poa.network
|
||||
# The "gasPrice" parameter set in every deployment/configuration transaction on Foreign network (in Wei).
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=1000000000
|
||||
# The address of the existing AMB bridge in the Foreign network that will be used to pass messages
|
||||
# to the Home network.
|
||||
FOREIGN_AMB_BRIDGE=0x0000000000000000000000000000000000000000
|
||||
# The gas limit that will be used in the execution of the message passed to the mediator contract
|
||||
# in the Home network.
|
||||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=1000000
|
||||
# Address on Foreign network with permissions to change parameters of the mediator contract.
|
||||
FOREIGN_MEDIATOR_OWNER=0x0000000000000000000000000000000000000000
|
||||
# Address on Foreign network with permissions to upgrade the mediator contract
|
||||
FOREIGN_UPGRADEABLE_ADMIN=0x0000000000000000000000000000000000000000
|
||||
|
||||
# Cryptokitties contract address on Foreign network. If not defined or set to address zero, the contract will be deployed on Foreign network.
|
||||
CRYPTOKITTIES_ADDRESS=0x0000000000000000000000000000000000000000
|
||||
# Amount of Kitties to Mint on Foreign network
|
||||
|
@ -37,6 +64,12 @@ Then
|
|||
yarn deploy
|
||||
```
|
||||
|
||||
#### Tests
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
|
||||
#### Flat contracts
|
||||
```bash
|
||||
yarn flatten
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 86b35f8382d5cb98e5738128a0c1a3718c471c72
|
|
@ -0,0 +1,5 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
interface IForeignMediator {
|
||||
function handleBridgedTokens(address _recipient, uint256 _tokenId, bytes32 _nonce) external;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
interface IHomeMediator {
|
||||
function handleBridgedTokens(address _recipient, uint256 _tokenId, bytes _metadata, bytes32 _nonce) external;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
interface ISimpleBridgeKitty {
|
||||
function mint(
|
||||
uint256 _tokenId,
|
||||
bool _isReady,
|
||||
uint256 _cooldownIndex,
|
||||
uint256 _nextActionAt,
|
||||
uint256 _siringWithId,
|
||||
uint256 _birthTime,
|
||||
uint256 _matronId,
|
||||
uint256 _sireId,
|
||||
uint256 _generation,
|
||||
uint256 _genes,
|
||||
address _owner
|
||||
) external;
|
||||
function burn(uint256 _tokenId) external;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../../bridge-contracts/contracts/upgradeability/EternalStorage.sol";
|
||||
import "../../bridge-contracts/contracts/interfaces/IAMB.sol";
|
||||
import "../../bridge-contracts/contracts/upgradeable_contracts/Ownable.sol";
|
||||
import "openzeppelin-solidity/contracts/AddressUtils.sol";
|
||||
|
||||
contract AMBMediator is EternalStorage, Ownable {
|
||||
bytes32 internal constant BRIDGE_CONTRACT = keccak256(abi.encodePacked("bridgeContract"));
|
||||
bytes32 internal constant MEDIATOR_CONTRACT = keccak256(abi.encodePacked("mediatorContract"));
|
||||
bytes32 internal constant REQUEST_GAS_LIMIT = keccak256(abi.encodePacked("requestGasLimit"));
|
||||
|
||||
function setBridgeContract(address _bridgeContract) external onlyOwner {
|
||||
_setBridgeContract(_bridgeContract);
|
||||
}
|
||||
|
||||
function _setBridgeContract(address _bridgeContract) internal {
|
||||
require(AddressUtils.isContract(_bridgeContract));
|
||||
addressStorage[BRIDGE_CONTRACT] = _bridgeContract;
|
||||
}
|
||||
|
||||
function bridgeContract() public view returns (IAMB) {
|
||||
return IAMB(addressStorage[BRIDGE_CONTRACT]);
|
||||
}
|
||||
|
||||
function setMediatorContractOnOtherSide(address _mediatorContract) external onlyOwner {
|
||||
_setMediatorContractOnOtherSide(_mediatorContract);
|
||||
}
|
||||
|
||||
function _setMediatorContractOnOtherSide(address _mediatorContract) internal {
|
||||
addressStorage[MEDIATOR_CONTRACT] = _mediatorContract;
|
||||
}
|
||||
|
||||
function mediatorContractOnOtherSide() public view returns (address) {
|
||||
return addressStorage[MEDIATOR_CONTRACT];
|
||||
}
|
||||
|
||||
function setRequestGasLimit(uint256 _requestGasLimit) external onlyOwner {
|
||||
_setRequestGasLimit(_requestGasLimit);
|
||||
}
|
||||
|
||||
function _setRequestGasLimit(uint256 _requestGasLimit) internal {
|
||||
require(_requestGasLimit <= bridgeContract().maxGasPerTx());
|
||||
uintStorage[REQUEST_GAS_LIMIT] = _requestGasLimit;
|
||||
}
|
||||
|
||||
function requestGasLimit() public view returns (uint256) {
|
||||
return uintStorage[REQUEST_GAS_LIMIT];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../../bridge-contracts/contracts/upgradeable_contracts/Initializable.sol";
|
||||
import "../../bridge-contracts/contracts/upgradeable_contracts/Claimable.sol";
|
||||
import "../../bridge-contracts/contracts/upgradeable_contracts/Upgradeable.sol";
|
||||
import "../../bridge-contracts/contracts/libraries/Bytes.sol";
|
||||
import "./AMBMediator.sol";
|
||||
import "./ERC721Bridge.sol";
|
||||
|
||||
contract BasicMediator is Initializable, AMBMediator, ERC721Bridge, Upgradeable, Claimable {
|
||||
event FailedMessageFixed(bytes32 indexed dataHash, address recipient, uint256 tokenId);
|
||||
|
||||
bytes32 internal constant NONCE = keccak256(abi.encodePacked("nonce"));
|
||||
bytes4 internal constant GET_KITTY = 0xe98b7f4d; // getKitty(uint256)
|
||||
|
||||
function initialize(
|
||||
address _bridgeContract,
|
||||
address _mediatorContract,
|
||||
address _erc721token,
|
||||
uint256 _requestGasLimit,
|
||||
address _owner
|
||||
) external returns (bool) {
|
||||
require(!isInitialized());
|
||||
|
||||
_setBridgeContract(_bridgeContract);
|
||||
_setMediatorContractOnOtherSide(_mediatorContract);
|
||||
setErc721token(_erc721token);
|
||||
_setRequestGasLimit(_requestGasLimit);
|
||||
setOwner(_owner);
|
||||
setNonce(keccak256(abi.encodePacked(address(this))));
|
||||
setInitialize();
|
||||
|
||||
return isInitialized();
|
||||
}
|
||||
|
||||
function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) {
|
||||
return (1, 0, 0);
|
||||
}
|
||||
|
||||
function getBridgeMode() external pure returns (bytes4 _data) {
|
||||
return bytes4(keccak256(abi.encodePacked("nft-to-nft-amb")));
|
||||
}
|
||||
|
||||
function transferToken(address _from, uint256 _tokenId) external {
|
||||
ERC721 token = erc721token();
|
||||
address to = address(this);
|
||||
|
||||
token.transferFrom(_from, to, _tokenId);
|
||||
bridgeSpecificActionsOnTokenTransfer(_from, _tokenId);
|
||||
}
|
||||
|
||||
/**
|
||||
* getKitty(uint256) returns:
|
||||
* bool isGestating,
|
||||
* bool isReady,
|
||||
* uint256 cooldownIndex,
|
||||
* uint256 nextActionAt,
|
||||
* uint256 siringWithId,
|
||||
* uint256 birthTime,
|
||||
* uint256 matronId,
|
||||
* uint256 sireId,
|
||||
* uint256 generation,
|
||||
* uint256 genes
|
||||
**/
|
||||
function getMetadata(uint256 _tokenId) internal view returns (bytes memory metadata) {
|
||||
bytes memory callData = abi.encodeWithSelector(GET_KITTY, _tokenId);
|
||||
address tokenAddress = erc721token();
|
||||
metadata = new bytes(320);
|
||||
assembly {
|
||||
let result := call(gas, tokenAddress, 0x0, add(callData, 0x20), mload(callData), 0, 0)
|
||||
returndatacopy(add(metadata, 0x20), 0, returndatasize)
|
||||
|
||||
switch result
|
||||
case 0 {
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function nonce() internal view returns (bytes32) {
|
||||
return Bytes.bytesToBytes32(bytesStorage[NONCE]);
|
||||
}
|
||||
|
||||
function setNonce(bytes32 _hash) internal {
|
||||
bytesStorage[NONCE] = abi.encodePacked(_hash);
|
||||
}
|
||||
|
||||
function setMessageHashTokenId(bytes32 _hash, uint256 _tokenId) internal {
|
||||
uintStorage[keccak256(abi.encodePacked("messageHashTokenId", _hash))] = _tokenId;
|
||||
}
|
||||
|
||||
function messageHashTokenId(bytes32 _hash) internal view returns (uint256) {
|
||||
return uintStorage[keccak256(abi.encodePacked("messageHashTokenId", _hash))];
|
||||
}
|
||||
|
||||
function setMessageHashRecipient(bytes32 _hash, address _recipient) internal {
|
||||
addressStorage[keccak256(abi.encodePacked("messageHashRecipient", _hash))] = _recipient;
|
||||
}
|
||||
|
||||
function messageHashRecipient(bytes32 _hash) internal view returns (address) {
|
||||
return addressStorage[keccak256(abi.encodePacked("messageHashRecipient", _hash))];
|
||||
}
|
||||
|
||||
function setMessageHashFixed(bytes32 _hash) internal {
|
||||
boolStorage[keccak256(abi.encodePacked("messageHashFixed", _hash))] = true;
|
||||
}
|
||||
|
||||
function messageHashFixed(bytes32 _hash) public view returns (bool) {
|
||||
return boolStorage[keccak256(abi.encodePacked("messageHashFixed", _hash))];
|
||||
}
|
||||
|
||||
function requestFailedMessageFix(bytes32 _txHash) external {
|
||||
require(!bridgeContract().messageCallStatus(_txHash));
|
||||
require(bridgeContract().failedMessageReceiver(_txHash) == address(this));
|
||||
require(bridgeContract().failedMessageSender(_txHash) == mediatorContractOnOtherSide());
|
||||
bytes32 dataHash = bridgeContract().failedMessageDataHash(_txHash);
|
||||
|
||||
bytes4 methodSelector = this.fixFailedMessage.selector;
|
||||
bytes memory data = abi.encodeWithSelector(methodSelector, dataHash);
|
||||
bridgeContract().requireToPassMessage(mediatorContractOnOtherSide(), data, requestGasLimit());
|
||||
}
|
||||
|
||||
function claimTokens(address _token, address _to) public onlyIfUpgradeabilityOwner validAddress(_to) {
|
||||
claimValues(_token, _to);
|
||||
}
|
||||
|
||||
function fixFailedMessage(bytes32 _dataHash) external;
|
||||
|
||||
function bridgeSpecificActionsOnTokenTransfer(address _from, uint256 _tokenId) internal;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../kitty/ERC721.sol";
|
||||
import "openzeppelin-solidity/contracts/AddressUtils.sol";
|
||||
import "../../bridge-contracts/contracts/upgradeability/EternalStorage.sol";
|
||||
|
||||
contract ERC721Bridge is EternalStorage {
|
||||
bytes32 internal constant ERC721_TOKEN = keccak256(abi.encodePacked("erc721token"));
|
||||
|
||||
function erc721token() public view returns (ERC721) {
|
||||
return ERC721(addressStorage[ERC721_TOKEN]);
|
||||
}
|
||||
|
||||
function setErc721token(address _token) internal {
|
||||
require(AddressUtils.isContract(_token));
|
||||
addressStorage[ERC721_TOKEN] = _token;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "./BasicMediator.sol";
|
||||
import "../interfaces/IHomeMediator.sol";
|
||||
|
||||
contract ForeignMediator is BasicMediator {
|
||||
function passMessage(address _from, uint256 _tokenId) internal {
|
||||
bytes memory metadata = getMetadata(_tokenId);
|
||||
|
||||
bytes4 methodSelector = IHomeMediator(0).handleBridgedTokens.selector;
|
||||
bytes memory data = abi.encodeWithSelector(methodSelector, _from, _tokenId, metadata, nonce());
|
||||
|
||||
bytes32 dataHash = keccak256(data);
|
||||
setMessageHashTokenId(dataHash, _tokenId);
|
||||
setMessageHashRecipient(dataHash, _from);
|
||||
setNonce(dataHash);
|
||||
|
||||
bridgeContract().requireToPassMessage(mediatorContractOnOtherSide(), data, requestGasLimit());
|
||||
}
|
||||
|
||||
function handleBridgedTokens(
|
||||
address _recipient,
|
||||
uint256 _tokenId,
|
||||
bytes32 /* _nonce */
|
||||
) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(bridgeContract().messageSender() == mediatorContractOnOtherSide());
|
||||
erc721token().transfer(_recipient, _tokenId);
|
||||
}
|
||||
|
||||
function bridgeSpecificActionsOnTokenTransfer(address _from, uint256 _tokenId) internal {
|
||||
passMessage(_from, _tokenId);
|
||||
}
|
||||
|
||||
function fixFailedMessage(bytes32 _dataHash) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(bridgeContract().messageSender() == mediatorContractOnOtherSide());
|
||||
require(!messageHashFixed(_dataHash));
|
||||
|
||||
address recipient = messageHashRecipient(_dataHash);
|
||||
uint256 tokenId = messageHashTokenId(_dataHash);
|
||||
|
||||
setMessageHashFixed(_dataHash);
|
||||
erc721token().transfer(recipient, tokenId);
|
||||
|
||||
emit FailedMessageFixed(_dataHash, recipient, tokenId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "./BasicMediator.sol";
|
||||
import "../interfaces/IForeignMediator.sol";
|
||||
import "../interfaces/ISimpleBridgeKitty.sol";
|
||||
|
||||
contract HomeMediator is BasicMediator {
|
||||
function passMessage(address _from, uint256 _tokenId, bytes _metadata) internal {
|
||||
bytes4 methodSelector = IForeignMediator(0).handleBridgedTokens.selector;
|
||||
bytes memory data = abi.encodeWithSelector(methodSelector, _from, _tokenId, nonce());
|
||||
|
||||
bytes32 dataHash = keccak256(data);
|
||||
setMessageHashTokenId(dataHash, _tokenId);
|
||||
setMessageHashRecipient(dataHash, _from);
|
||||
setMessageHashMetadata(dataHash, _metadata);
|
||||
setNonce(dataHash);
|
||||
|
||||
bridgeContract().requireToPassMessage(mediatorContractOnOtherSide(), data, requestGasLimit());
|
||||
}
|
||||
|
||||
function handleBridgedTokens(
|
||||
address _recipient,
|
||||
uint256 _tokenId,
|
||||
bytes _metadata,
|
||||
bytes32 /* _nonce */
|
||||
) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(bridgeContract().messageSender() == mediatorContractOnOtherSide());
|
||||
|
||||
mintToken(_recipient, _tokenId, _metadata);
|
||||
}
|
||||
|
||||
function mintToken(address _recipient, uint256 _tokenId, bytes _metadata) internal {
|
||||
ISimpleBridgeKitty(erc721token()).mint(
|
||||
_tokenId,
|
||||
getMetadataBoolValue(_metadata, 2), // isReady
|
||||
getMetadataUintValue(_metadata, 3), // cooldownIndex
|
||||
getMetadataUintValue(_metadata, 4), // nextActionAt
|
||||
getMetadataUintValue(_metadata, 5), // siringWithId
|
||||
getMetadataUintValue(_metadata, 6), // birthTime
|
||||
getMetadataUintValue(_metadata, 7), // matronId
|
||||
getMetadataUintValue(_metadata, 8), // sireId
|
||||
getMetadataUintValue(_metadata, 9), // generation
|
||||
getMetadataUintValue(_metadata, 10), // genes
|
||||
_recipient
|
||||
);
|
||||
}
|
||||
|
||||
function bridgeSpecificActionsOnTokenTransfer(address _from, uint256 _tokenId) internal {
|
||||
bytes memory metadata = getMetadata(_tokenId);
|
||||
|
||||
ISimpleBridgeKitty(erc721token()).burn(_tokenId);
|
||||
passMessage(_from, _tokenId, metadata);
|
||||
}
|
||||
|
||||
function getMetadataUintValue(bytes _data, uint256 _index) internal pure returns (uint256 value) {
|
||||
uint256 offset = 32 * _index;
|
||||
assembly {
|
||||
value := mload(add(_data, offset))
|
||||
}
|
||||
}
|
||||
|
||||
function getMetadataBoolValue(bytes _data, uint256 _index) internal pure returns (bool value) {
|
||||
uint256 offset = 32 * _index;
|
||||
assembly {
|
||||
value := mload(add(_data, offset))
|
||||
}
|
||||
}
|
||||
|
||||
function setMessageHashMetadata(bytes32 _hash, bytes _metadata) internal {
|
||||
bytesStorage[keccak256(abi.encodePacked("messageHashMetadata", _hash))] = _metadata;
|
||||
}
|
||||
|
||||
function messageHashMetadata(bytes32 _hash) internal view returns (bytes) {
|
||||
return bytesStorage[keccak256(abi.encodePacked("messageHashMetadata", _hash))];
|
||||
}
|
||||
|
||||
function fixFailedMessage(bytes32 _dataHash) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(bridgeContract().messageSender() == mediatorContractOnOtherSide());
|
||||
require(!messageHashFixed(_dataHash));
|
||||
|
||||
address recipient = messageHashRecipient(_dataHash);
|
||||
uint256 tokenId = messageHashTokenId(_dataHash);
|
||||
bytes memory metadata = messageHashMetadata(_dataHash);
|
||||
|
||||
setMessageHashFixed(_dataHash);
|
||||
mintToken(recipient, tokenId, metadata);
|
||||
|
||||
emit FailedMessageFixed(_dataHash, recipient, tokenId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
contract AMBMock {
|
||||
event MockedEvent(bytes encodedData);
|
||||
|
||||
address public messageSender;
|
||||
uint256 public maxGasPerTx;
|
||||
bytes32 public transactionHash;
|
||||
mapping(bytes32 => bool) public messageCallStatus;
|
||||
mapping(bytes32 => address) public failedMessageSender;
|
||||
mapping(bytes32 => address) public failedMessageReceiver;
|
||||
mapping(bytes32 => bytes32) public failedMessageDataHash;
|
||||
|
||||
function setMaxGasPerTx(uint256 _value) public {
|
||||
maxGasPerTx = _value;
|
||||
}
|
||||
|
||||
function executeMessageCall(address _contract, address _sender, bytes _data, bytes32 _txHash, uint256 _gas) public {
|
||||
messageSender = _sender;
|
||||
transactionHash = _txHash;
|
||||
bool status = _contract.call.gas(_gas)(_data);
|
||||
messageSender = address(0);
|
||||
transactionHash = bytes32(0);
|
||||
|
||||
messageCallStatus[_txHash] = status;
|
||||
if (!status) {
|
||||
failedMessageDataHash[_txHash] = keccak256(_data);
|
||||
failedMessageReceiver[_txHash] = _contract;
|
||||
failedMessageSender[_txHash] = _sender;
|
||||
}
|
||||
}
|
||||
|
||||
function requireToPassMessage(address _contract, bytes _data, uint256 _gas) public {
|
||||
emit MockedEvent(abi.encodePacked(msg.sender, _contract, _gas, uint8(0x00), _data));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
contract BridgeRole {
|
||||
address public bridge;
|
||||
|
||||
function BridgeRole() public {
|
||||
bridge = msg.sender;
|
||||
}
|
||||
|
||||
modifier onlyBridge() {
|
||||
require(msg.sender == bridge);
|
||||
_;
|
||||
}
|
||||
|
||||
function transferBridgeRole(address newBridge) external onlyBridge {
|
||||
if (newBridge != address(0)) {
|
||||
bridge = newBridge;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "./SimpleKittyCore.sol";
|
||||
import "./BridgeRole.sol";
|
||||
|
||||
contract SimpleBridgeKitty is BridgeRole, SimpleKittyCore {
|
||||
event Death(uint256 kittyId);
|
||||
|
||||
function mint(
|
||||
uint256 _tokenId,
|
||||
bool _isReady,
|
||||
uint256 _cooldownIndex,
|
||||
uint256 _nextActionAt,
|
||||
uint256 _siringWithId,
|
||||
uint256 _birthTime,
|
||||
uint256 _matronId,
|
||||
uint256 _sireId,
|
||||
uint256 _generation,
|
||||
uint256 _genes,
|
||||
address _owner
|
||||
) external onlyBridge {
|
||||
_createKitty(
|
||||
_tokenId,
|
||||
_isReady,
|
||||
_cooldownIndex,
|
||||
_nextActionAt,
|
||||
_siringWithId,
|
||||
_birthTime,
|
||||
_matronId,
|
||||
_sireId,
|
||||
_generation,
|
||||
_genes,
|
||||
_owner
|
||||
);
|
||||
}
|
||||
|
||||
function burn(uint256 _tokenId) external onlyBridge {
|
||||
require(_owns(msg.sender, _tokenId));
|
||||
// remove kitty
|
||||
delete kitties[_tokenId];
|
||||
// reduce total supply
|
||||
kittyTotalSupply--;
|
||||
// remove ownership of the kitty
|
||||
delete kittyIndexToOwner[_tokenId];
|
||||
// reduce owner token account
|
||||
ownershipTokenCount[msg.sender]--;
|
||||
emit Death(_tokenId);
|
||||
}
|
||||
}
|
|
@ -87,7 +87,7 @@ contract SimpleKittyBase {
|
|||
delete kittyIndexToApproved[_tokenId];
|
||||
}
|
||||
// Emit the transfer event.
|
||||
Transfer(_from, _to, _tokenId);
|
||||
emit Transfer(_from, _to, _tokenId);
|
||||
}
|
||||
|
||||
/// @dev An internal method that creates a new kitty and stores it.
|
||||
|
@ -130,7 +130,7 @@ contract SimpleKittyBase {
|
|||
kittyTotalSupply++;
|
||||
|
||||
// emit the birth event
|
||||
Birth(_owner, _tokenId, uint256(_kitty.matronId), uint256(_kitty.sireId), _kitty.genes);
|
||||
emit Birth(_owner, _tokenId, uint256(_kitty.matronId), uint256(_kitty.sireId), _kitty.genes);
|
||||
|
||||
// This will assign ownership, and also emit the Transfer event as
|
||||
// per ERC721 draft
|
||||
|
|
|
@ -100,7 +100,7 @@ contract SimpleKittyOwnership is SimpleKittyBase, ERC721 {
|
|||
_approve(_tokenId, _to);
|
||||
|
||||
// Emit approval event.
|
||||
Approval(msg.sender, _to, _tokenId);
|
||||
emit Approval(msg.sender, _to, _tokenId);
|
||||
}
|
||||
|
||||
/// @notice Transfer a Kitty owned by another address, for which the calling address
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../../bridge-contracts/contracts/upgradeability/EternalStorage.sol";
|
||||
import "../../bridge-contracts/contracts/upgradeability/OwnedUpgradeabilityProxy.sol";
|
||||
|
||||
/**
|
||||
* @title EternalStorageProxy
|
||||
* @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
|
||||
* Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic
|
||||
* authorization control functionalities
|
||||
*/
|
||||
// solhint-disable-next-line no-empty-blocks
|
||||
contract EternalStorageProxy is EternalStorage, OwnedUpgradeabilityProxy {}
|
|
@ -195,8 +195,8 @@ async function transferProxyOwnership({ proxy, newOwner, nonce, url }) {
|
|||
assert.strictEqual(Web3Utils.hexToNumber(result.status), 1, 'Transaction Failed')
|
||||
}
|
||||
|
||||
async function transferOwnership({ contract, newOwner, nonce, url }) {
|
||||
const data = await contract.methods.transferOwnership(newOwner).encodeABI()
|
||||
async function transferBridgeRole({ contract, newOwner, nonce, url }) {
|
||||
const data = await contract.methods.transferBridgeRole(newOwner).encodeABI()
|
||||
const sendTx = getSendTxMethod(url)
|
||||
const result = await sendTx({
|
||||
data,
|
||||
|
@ -212,6 +212,23 @@ function getSendTxMethod(url) {
|
|||
return url === HOME_RPC_URL ? sendRawTxHome : sendRawTxForeign
|
||||
}
|
||||
|
||||
const kittyGenes = [
|
||||
'461318606473215840474968038713412278792841200610013055040007908862876014',
|
||||
'512901570795906580689310042942192685302828800779079863559617410586062221',
|
||||
'456123814460010858370996918295523736710893964491134685860209207735135662',
|
||||
'623347887640772406912616247570945954300391612998914019334437441678817675',
|
||||
'623327769702602566148110117488157993388740168622469003102795169878388139',
|
||||
'516517468280385478902426949869045996034332056826094284352752576550613389',
|
||||
'461303492617760133525700505199185512215446852893161871027943135841299852',
|
||||
'516350753935039751872377244600559998302956049058903453328289625078609292',
|
||||
'516350706387040918774838507181320473800432871454139137641571763026803053',
|
||||
'623315925723314209099500395652252912896485976535230435375457673062846598'
|
||||
]
|
||||
|
||||
function getKittyGene(i) {
|
||||
return kittyGenes[i % kittyGenes.length]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
deployContract,
|
||||
sendRawTxHome,
|
||||
|
@ -219,8 +236,9 @@ module.exports = {
|
|||
privateKeyToAddress,
|
||||
upgradeProxy,
|
||||
transferProxyOwnership,
|
||||
transferOwnership,
|
||||
transferBridgeRole,
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
deploymentPrivateKey
|
||||
deploymentPrivateKey,
|
||||
getKittyGene
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
const assert = require('assert')
|
||||
const Web3Utils = require('web3-utils')
|
||||
const kittyCore = require('../build/contracts/KittyCore')
|
||||
const EternalStorageProxy = require('../build/contracts/EternalStorageProxy')
|
||||
const ForeignMediator = require('../build/contracts/ForeignMediator')
|
||||
const {
|
||||
deployContract,
|
||||
privateKeyToAddress,
|
||||
web3Foreign,
|
||||
deploymentPrivateKey,
|
||||
sendRawTxForeign
|
||||
sendRawTxForeign,
|
||||
upgradeProxy,
|
||||
getKittyGene
|
||||
} = require('./deploymentUtils')
|
||||
|
||||
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY, CRYPTOKITTIES_ADDRESS, KITTIES_AMOUNT, FOREIGN_RPC_URL } = process.env
|
||||
|
@ -18,7 +22,7 @@ async function deployForeign() {
|
|||
|
||||
if (!CRYPTOKITTIES_ADDRESS || CRYPTOKITTIES_ADDRESS === '0x0000000000000000000000000000000000000000') {
|
||||
// deploy contract
|
||||
console.log('[Foreign] Deploying kittyCore contract')
|
||||
console.log('\n[Foreign] Deploying kittyCore contract')
|
||||
const kittyCoreContract = await deployContract(kittyCore, [], {
|
||||
from: accountAddress,
|
||||
network: 'foreign',
|
||||
|
@ -31,9 +35,10 @@ async function deployForeign() {
|
|||
// mint kitties
|
||||
const kittiesAmount = Number(KITTIES_AMOUNT)
|
||||
if (kittiesAmount) {
|
||||
console.log('[Foreign] Minting kitties to', accountAddress)
|
||||
for (let i = 1; i <= kittiesAmount; i++) {
|
||||
const mintData = await kittyCoreContract.methods.createPromoKitty(i, accountAddress).encodeABI()
|
||||
console.log('\n[Foreign] Minting kitties to', accountAddress)
|
||||
for (let i = 0; i < kittiesAmount; i++) {
|
||||
const gene = getKittyGene(i)
|
||||
const mintData = await kittyCoreContract.methods.createPromoKitty(gene, accountAddress).encodeABI()
|
||||
const txMint = await sendRawTxForeign({
|
||||
data: mintData,
|
||||
nonce,
|
||||
|
@ -42,13 +47,41 @@ async function deployForeign() {
|
|||
url: FOREIGN_RPC_URL
|
||||
})
|
||||
assert.strictEqual(Web3Utils.hexToNumber(txMint.status), 1, 'Transaction Failed')
|
||||
console.log(`[Foreign] Minted Kitty ID: ${i}`)
|
||||
console.log(`[Foreign] Minted Kitty ID: ${i + 1}`)
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n[Foreign] Deploying ForeignMediator storage')
|
||||
const foreignMediatorStorage = await deployContract(EternalStorageProxy, [], {
|
||||
from: accountAddress,
|
||||
network: 'foreign',
|
||||
nonce
|
||||
})
|
||||
nonce++
|
||||
console.log('[Foreign] ForeignMediator Storage: ', foreignMediatorStorage.options.address)
|
||||
|
||||
console.log('\n[Foreign] Deploying ForeignMediator implementation')
|
||||
const foreignBridgeImplementation = await deployContract(ForeignMediator, [], {
|
||||
from: accountAddress,
|
||||
network: 'foreign',
|
||||
nonce
|
||||
})
|
||||
nonce++
|
||||
console.log('[Foreign] ForeignMediator Implementation: ', foreignBridgeImplementation.options.address)
|
||||
|
||||
console.log('\n[Foreign] Hooking up ForeignMediator storage to ForeignMediator implementation')
|
||||
await upgradeProxy({
|
||||
proxy: foreignMediatorStorage,
|
||||
implementationAddress: foreignBridgeImplementation.options.address,
|
||||
version: '1',
|
||||
nonce,
|
||||
url: FOREIGN_RPC_URL
|
||||
})
|
||||
|
||||
return {
|
||||
foreignMediator: foreignMediatorStorage.options.address,
|
||||
kittyCore: kittyCoreAddress
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,61 @@
|
|||
const simpleKittyCore = require('../build/contracts/SimpleKittyCore')
|
||||
const { deployContract, privateKeyToAddress, web3Home } = require('./deploymentUtils')
|
||||
const SimpleBridgeKitty = require('../build/contracts/SimpleBridgeKitty')
|
||||
const EternalStorageProxy = require('../build/contracts/EternalStorageProxy')
|
||||
const HomeMediator = require('../build/contracts/HomeMediator')
|
||||
const { deployContract, privateKeyToAddress, web3Home, upgradeProxy, transferBridgeRole } = require('./deploymentUtils')
|
||||
|
||||
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
|
||||
const { HOME_RPC_URL, DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
|
||||
|
||||
async function deployHome() {
|
||||
const accountAddress = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
|
||||
const nonce = await web3Home.eth.getTransactionCount(accountAddress)
|
||||
let nonce = await web3Home.eth.getTransactionCount(accountAddress)
|
||||
|
||||
// deploy contract
|
||||
console.log('[Home] Deploying simpleKittyCore contract')
|
||||
const simpleKittyCoreContract = await deployContract(simpleKittyCore, [], {
|
||||
console.log('\n[Home] Deploying HomeMediator storage')
|
||||
const homeMediatorStorage = await deployContract(EternalStorageProxy, [], {
|
||||
from: accountAddress,
|
||||
nonce
|
||||
})
|
||||
nonce++
|
||||
console.log('[Home] HomeMediator Storage: ', homeMediatorStorage.options.address)
|
||||
|
||||
console.log('\n[Home] Deploying HomeMediator implementation')
|
||||
const homeBridgeImplementation = await deployContract(HomeMediator, [], {
|
||||
from: accountAddress,
|
||||
nonce
|
||||
})
|
||||
nonce++
|
||||
console.log('[Home] HomeMediator Implementation: ', homeBridgeImplementation.options.address)
|
||||
|
||||
console.log('\n[Home] Hooking up HomeMediator storage to HomeMediator implementation')
|
||||
await upgradeProxy({
|
||||
proxy: homeMediatorStorage,
|
||||
implementationAddress: homeBridgeImplementation.options.address,
|
||||
version: '1',
|
||||
nonce,
|
||||
url: HOME_RPC_URL
|
||||
})
|
||||
nonce++
|
||||
|
||||
// deploy token contract
|
||||
console.log('\n[Home] Deploying simpleKittyCore contract')
|
||||
const simpleKittyContract = await deployContract(SimpleBridgeKitty, [], {
|
||||
from: accountAddress,
|
||||
network: 'home',
|
||||
nonce
|
||||
})
|
||||
console.log('[Home] simpleKittyCore Address: ', simpleKittyCoreContract.options.address)
|
||||
nonce++
|
||||
console.log('[Home] simpleKittyCore Address: ', simpleKittyContract.options.address)
|
||||
|
||||
console.log('\n[Home] Transferring ownership of Bridgeable token to HomeMediator contract')
|
||||
await transferBridgeRole({
|
||||
contract: simpleKittyContract,
|
||||
newOwner: homeMediatorStorage.options.address,
|
||||
nonce,
|
||||
url: HOME_RPC_URL
|
||||
})
|
||||
|
||||
return {
|
||||
simpleKittyCore: simpleKittyCoreContract.options.address
|
||||
homeMediator: homeMediatorStorage.options.address,
|
||||
simpleKitty: simpleKittyContract.options.address
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
const deployHome = require('./home')
|
||||
const deployForeign = require('./foreign')
|
||||
const initialize = require('./initialize')
|
||||
|
||||
async function main() {
|
||||
const { simpleKittyCore } = await deployHome()
|
||||
const { kittyCore } = await deployForeign()
|
||||
const { homeMediator, simpleKitty } = await deployHome()
|
||||
const { foreignMediator, kittyCore } = await deployForeign()
|
||||
await initialize({ homeMediator, foreignMediator, homeKitty: simpleKitty, foreignKitty: kittyCore })
|
||||
console.log('\nDeployment has been completed.\n')
|
||||
console.log(`[ Home ] simpleKittyCore: ${simpleKittyCore}`)
|
||||
console.log(`[ Home ] homeMediator: ${homeMediator}`)
|
||||
console.log(`[ Home ] simpleKittyCore: ${simpleKitty}`)
|
||||
console.log(`[ Foreign ] foreignMediator: ${foreignMediator}`)
|
||||
console.log(`[ Foreign ] kittyCore: ${kittyCore}`)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
const Web3Utils = require('web3-utils')
|
||||
const assert = require('assert')
|
||||
|
||||
const EternalStorageProxy = require('../build/contracts/EternalStorageProxy')
|
||||
const HomeMediator = require('../build/contracts/HomeMediator')
|
||||
const ForeignMediator = require('../build/contracts/ForeignMediator')
|
||||
|
||||
const {
|
||||
privateKeyToAddress,
|
||||
sendRawTxHome,
|
||||
sendRawTxForeign,
|
||||
transferProxyOwnership,
|
||||
web3Home,
|
||||
web3Foreign,
|
||||
deploymentPrivateKey
|
||||
} = require('./deploymentUtils')
|
||||
|
||||
const {
|
||||
HOME_RPC_URL,
|
||||
HOME_AMB_BRIDGE,
|
||||
HOME_MEDIATOR_REQUEST_GAS_LIMIT,
|
||||
HOME_MEDIATOR_OWNER,
|
||||
HOME_UPGRADEABLE_ADMIN,
|
||||
FOREIGN_RPC_URL,
|
||||
FOREIGN_MEDIATOR_OWNER,
|
||||
FOREIGN_UPGRADEABLE_ADMIN,
|
||||
FOREIGN_AMB_BRIDGE,
|
||||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT,
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY
|
||||
} = process.env
|
||||
|
||||
const accountAddress = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
|
||||
|
||||
async function initialize({
|
||||
web3,
|
||||
url,
|
||||
address,
|
||||
abi,
|
||||
proxyAbi,
|
||||
params: { bridgeContract, mediatorContract, token, requestGasLimit, owner },
|
||||
upgradeableAdmin,
|
||||
sendRawTx
|
||||
}) {
|
||||
let nonce = await web3.eth.getTransactionCount(accountAddress)
|
||||
|
||||
const contract = new web3.eth.Contract(abi, address)
|
||||
console.log(`
|
||||
AMB contract: ${bridgeContract},
|
||||
Mediator contract: ${mediatorContract},
|
||||
Token contract: ${token},
|
||||
MEDIATOR_REQUEST_GAS_LIMIT : ${requestGasLimit},
|
||||
OWNER: ${owner}
|
||||
`)
|
||||
|
||||
const initializeData = await contract.methods
|
||||
.initialize(bridgeContract, mediatorContract, token, requestGasLimit, owner)
|
||||
.encodeABI()
|
||||
const txInitialize = await sendRawTx({
|
||||
data: initializeData,
|
||||
nonce,
|
||||
to: address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url
|
||||
})
|
||||
|
||||
assert.strictEqual(Web3Utils.hexToNumber(txInitialize.status), 1, 'Transaction Failed')
|
||||
nonce++
|
||||
|
||||
console.log('\nTransferring mediator proxy ownership to upgradeable admin')
|
||||
const proxy = new web3.eth.Contract(proxyAbi, address)
|
||||
await transferProxyOwnership({
|
||||
proxy,
|
||||
newOwner: upgradeableAdmin,
|
||||
nonce,
|
||||
url
|
||||
})
|
||||
}
|
||||
|
||||
async function initializeMediators({ homeMediator, foreignMediator, homeKitty, foreignKitty }) {
|
||||
console.log('\n[Home] Initializing Home Mediator with following parameters:')
|
||||
await initialize({
|
||||
web3: web3Home,
|
||||
url: HOME_RPC_URL,
|
||||
address: homeMediator,
|
||||
abi: HomeMediator.abi,
|
||||
proxyAbi: EternalStorageProxy.abi,
|
||||
params: {
|
||||
bridgeContract: HOME_AMB_BRIDGE,
|
||||
mediatorContract: foreignMediator,
|
||||
token: homeKitty,
|
||||
requestGasLimit: HOME_MEDIATOR_REQUEST_GAS_LIMIT,
|
||||
owner: HOME_MEDIATOR_OWNER
|
||||
},
|
||||
upgradeableAdmin: HOME_UPGRADEABLE_ADMIN,
|
||||
sendRawTx: sendRawTxHome
|
||||
})
|
||||
|
||||
console.log('\n[Foreign] Initializing Foreign Mediator with following parameters:')
|
||||
await initialize({
|
||||
web3: web3Foreign,
|
||||
url: FOREIGN_RPC_URL,
|
||||
address: foreignMediator,
|
||||
abi: ForeignMediator.abi,
|
||||
proxyAbi: EternalStorageProxy.abi,
|
||||
params: {
|
||||
bridgeContract: FOREIGN_AMB_BRIDGE,
|
||||
mediatorContract: homeMediator,
|
||||
token: foreignKitty,
|
||||
requestGasLimit: FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT,
|
||||
owner: FOREIGN_MEDIATOR_OWNER
|
||||
},
|
||||
upgradeableAdmin: FOREIGN_UPGRADEABLE_ADMIN,
|
||||
sendRawTx: sendRawTxForeign
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = initializeMediators
|
|
@ -11,24 +11,28 @@
|
|||
"lint:js": "eslint .",
|
||||
"lint:js:fix": "eslint . --fix",
|
||||
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
|
||||
"lint:sol:prettier:fix": "prettier --write **/*.sol"
|
||||
"lint:sol:prettier:fix": "prettier --write **/*.sol",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0",
|
||||
"dotenv": "^8.1.0",
|
||||
"ethereumjs-tx": "1.3.7",
|
||||
"node-fetch": "^2.6.0",
|
||||
"openzeppelin-solidity": "1.12.0",
|
||||
"truffle": "^5.0.35",
|
||||
"web3": "^1.2.1",
|
||||
"web3-utils": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.4.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^6.3.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-node": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"openzeppelin-test-helpers": "^0.4.3",
|
||||
"prettier": "^1.18.2",
|
||||
"prettier-plugin-solidity": "^1.0.0-alpha.32",
|
||||
"solhint": "^2.2.0",
|
||||
|
|
|
@ -6,14 +6,13 @@ fi
|
|||
|
||||
mkdir -p flats/kitty
|
||||
mkdir -p flats/simpleKitty
|
||||
mkdir -p flats/mediator
|
||||
mkdir -p flats/upgradeability
|
||||
|
||||
FLATTENER=./node_modules/.bin/truffle-flattener
|
||||
KITTY_CONTRACTS_DIR=contracts/kitty
|
||||
|
||||
|
||||
echo "Flattening kitty contracts"
|
||||
${FLATTENER} ${KITTY_CONTRACTS_DIR}/KittyCore.sol > flats/kitty/KittyCore_flat.sol
|
||||
${FLATTENER} ${KITTY_CONTRACTS_DIR}/GeneScience.sol > flats/kitty/GeneScience_flat.sol
|
||||
${FLATTENER} ${KITTY_CONTRACTS_DIR}/SaleClockAuction.sol > flats/kitty/SaleClockAuction_flat.sol
|
||||
${FLATTENER} ${KITTY_CONTRACTS_DIR}/SiringClockAuction.sol > flats/kitty/SiringClockAuction_flat.sol
|
||||
${FLATTENER} contracts/simpleKitty/SimpleKittyCore.sol > flats/simpleKitty/SimpleKittyCore_flat.sol
|
||||
${FLATTENER} contracts/kitty/KittyCore.sol > flats/kitty/KittyCore_flat.sol
|
||||
${FLATTENER} contracts/simpleKitty/SimpleBridgeKitty.sol > flats/simpleKitty/SimpleBridgeKitty_flat.sol
|
||||
${FLATTENER} contracts/mediator/HomeMediator.sol > flats/mediator/HomeMediator_flat.sol
|
||||
${FLATTENER} contracts/mediator/ForeignMediator.sol > flats/mediator/ForeignMediator_flat.sol
|
||||
${FLATTENER} contracts/upgradeability/EternalStorageProxy.sol > flats/upgradeability/EternalStorageProxy_flat.sol
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
const SimpleBridgeKitty = artifacts.require('SimpleBridgeKitty.sol')
|
||||
const AMBMock = artifacts.require('AMBMock.sol')
|
||||
|
||||
const { expect } = require('chai')
|
||||
const { ether, constants, expectRevert } = require('openzeppelin-test-helpers')
|
||||
|
||||
const {
|
||||
maxGasPerTx,
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
metadata,
|
||||
exampleTxHash,
|
||||
nonce
|
||||
} = require('./helpers')
|
||||
|
||||
function shouldBehaveLikeBasicMediator(accounts) {
|
||||
describe('shouldBehaveLikeBasicMediator', () => {
|
||||
let bridgeContract
|
||||
let erc721token
|
||||
const owner = accounts[0]
|
||||
describe('initialize', () => {
|
||||
beforeEach(async () => {
|
||||
bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
erc721token = await SimpleBridgeKitty.new()
|
||||
})
|
||||
it('should initialize', async function() {
|
||||
const contract = this.bridge
|
||||
const mediatorContractOnOtherSide = await this.mediatorContractOnOtherSide.new()
|
||||
|
||||
expect(await contract.isInitialized()).to.be.equal(false)
|
||||
expect(await contract.bridgeContract()).to.be.equal(constants.ZERO_ADDRESS)
|
||||
expect(await contract.mediatorContractOnOtherSide()).to.be.equal(constants.ZERO_ADDRESS)
|
||||
expect(await contract.erc721token()).to.be.equal(constants.ZERO_ADDRESS)
|
||||
expect(await contract.requestGasLimit()).to.be.bignumber.equal('0')
|
||||
expect(await contract.owner()).to.be.equal(constants.ZERO_ADDRESS)
|
||||
|
||||
// not valid bridge contract
|
||||
await expectRevert.unspecified(
|
||||
contract.initialize(
|
||||
constants.ZERO_ADDRESS,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
)
|
||||
|
||||
// not valid erc721 contract
|
||||
await expectRevert.unspecified(
|
||||
contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
constants.ZERO_ADDRESS,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
)
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
// already initialized
|
||||
await expectRevert.unspecified(
|
||||
contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
)
|
||||
|
||||
expect(await contract.isInitialized()).to.be.equal(true)
|
||||
expect(await contract.bridgeContract()).to.be.equal(bridgeContract.address)
|
||||
expect(await contract.mediatorContractOnOtherSide()).to.be.equal(mediatorContractOnOtherSide.address)
|
||||
expect(await contract.erc721token()).to.be.equal(erc721token.address)
|
||||
expect(await contract.requestGasLimit()).to.be.bignumber.equal(maxGasPerTx)
|
||||
expect(await contract.owner()).to.be.equal(owner)
|
||||
})
|
||||
it('only owner can set bridge contract', async function() {
|
||||
const contract = this.bridge
|
||||
const mediatorContractOnOtherSide = await this.mediatorContractOnOtherSide.new()
|
||||
const user = accounts[1]
|
||||
const notAContractAddress = accounts[2]
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
expect(await contract.bridgeContract()).to.be.equal(bridgeContract.address)
|
||||
|
||||
const newBridgeContract = await AMBMock.new()
|
||||
|
||||
await expectRevert.unspecified(contract.setBridgeContract(newBridgeContract.address, { from: user }))
|
||||
await expectRevert.unspecified(contract.setBridgeContract(notAContractAddress, { from: owner }))
|
||||
|
||||
await contract.setBridgeContract(newBridgeContract.address, { from: owner })
|
||||
expect(await contract.bridgeContract()).to.be.equal(newBridgeContract.address)
|
||||
})
|
||||
it('only owner can set mediator contract', async function() {
|
||||
const contract = this.bridge
|
||||
const mediatorContractOnOtherSide = await this.mediatorContractOnOtherSide.new()
|
||||
const user = accounts[1]
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
expect(await contract.bridgeContract()).to.be.equal(bridgeContract.address)
|
||||
|
||||
const newMediatorContract = await this.mediatorContractOnOtherSide.new()
|
||||
|
||||
await expectRevert.unspecified(
|
||||
contract.setMediatorContractOnOtherSide(newMediatorContract.address, { from: user })
|
||||
)
|
||||
|
||||
await contract.setMediatorContractOnOtherSide(newMediatorContract.address, { from: owner })
|
||||
expect(await contract.mediatorContractOnOtherSide()).to.be.equal(newMediatorContract.address)
|
||||
})
|
||||
it('only owner can set request Gas Limit', async function() {
|
||||
const contract = this.bridge
|
||||
const mediatorContractOnOtherSide = await this.mediatorContractOnOtherSide.new()
|
||||
const user = accounts[1]
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
expect(await contract.requestGasLimit()).to.be.bignumber.equal(maxGasPerTx)
|
||||
|
||||
const newMaxGasPerTx = ether('0.5')
|
||||
const invalidMaxGasPerTx = ether('1.5')
|
||||
|
||||
await expectRevert.unspecified(contract.setRequestGasLimit(newMaxGasPerTx, { from: user }))
|
||||
|
||||
// invalidMaxGasPerTx > bridgeContract.maxGasPerTx
|
||||
await expectRevert.unspecified(contract.setRequestGasLimit(invalidMaxGasPerTx, { from: owner }))
|
||||
|
||||
await contract.setRequestGasLimit(newMaxGasPerTx, { from: owner })
|
||||
expect(await contract.requestGasLimit()).to.be.bignumber.equal(newMaxGasPerTx)
|
||||
})
|
||||
})
|
||||
describe('getBridgeMode', () => {
|
||||
it('should return bridge mode and interface', async function() {
|
||||
const contract = this.bridge
|
||||
const bridgeModeHash = '0xb64f0fee' // 4 bytes of keccak256('nft-to-nft-amb')
|
||||
expect(await contract.getBridgeMode()).to.be.equal(bridgeModeHash)
|
||||
|
||||
const { major, minor, patch } = await contract.getBridgeInterfacesVersion()
|
||||
expect(major).to.be.bignumber.gte('0')
|
||||
expect(minor).to.be.bignumber.gte('0')
|
||||
expect(patch).to.be.bignumber.gte('0')
|
||||
})
|
||||
})
|
||||
describe('requestFailedMessageFix', () => {
|
||||
let contract
|
||||
let mediatorContractOnOtherSide
|
||||
let data
|
||||
const user = accounts[1]
|
||||
beforeEach(async function() {
|
||||
bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
erc721token = await SimpleBridgeKitty.new()
|
||||
|
||||
contract = this.bridge
|
||||
mediatorContractOnOtherSide = await this.mediatorContractOnOtherSide.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
erc721token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
try {
|
||||
data = await contract.contract.methods.handleBridgedTokens(user, tokenId, nonce).encodeABI()
|
||||
await erc721token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
contract.address,
|
||||
{ from: owner }
|
||||
)
|
||||
} catch (e) {
|
||||
data = await contract.contract.methods.handleBridgedTokens(user, tokenId, metadata, nonce).encodeABI()
|
||||
await erc721token.transferBridgeRole(contract.address, { from: owner })
|
||||
}
|
||||
})
|
||||
it('should allow to request a failed message fix', async () => {
|
||||
// Given
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
100
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
|
||||
const dataHash = await bridgeContract.failedMessageDataHash(exampleTxHash)
|
||||
|
||||
// When
|
||||
const { tx } = await contract.requestFailedMessageFix(exampleTxHash)
|
||||
|
||||
// Then
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
expect(logs.length).to.be.equal(1)
|
||||
expect(logs[0].args.encodedData.includes(dataHash.replace(/^0x/, ''))).to.be.equal(true)
|
||||
})
|
||||
it('should be a failed transaction', async () => {
|
||||
// Given
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(true)
|
||||
|
||||
// When
|
||||
await expectRevert.unspecified(contract.requestFailedMessageFix(exampleTxHash))
|
||||
})
|
||||
it('should be the receiver of the failed transaction', async () => {
|
||||
// Given
|
||||
await bridgeContract.executeMessageCall(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
await expectRevert.unspecified(contract.requestFailedMessageFix(exampleTxHash))
|
||||
})
|
||||
it('message sender should be mediator from other side', async () => {
|
||||
// Given
|
||||
await bridgeContract.executeMessageCall(contract.address, contract.address, data, exampleTxHash, 1000000)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
await expectRevert.unspecified(contract.requestFailedMessageFix(exampleTxHash))
|
||||
})
|
||||
it('should allow to request a fix multiple times', async () => {
|
||||
// Given
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
100
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
|
||||
const dataHash = await bridgeContract.failedMessageDataHash(exampleTxHash)
|
||||
|
||||
const { tx } = await contract.requestFailedMessageFix(exampleTxHash)
|
||||
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
expect(logs.length).to.be.equal(1)
|
||||
expect(logs[0].args.encodedData.includes(dataHash.replace(/^0x/, ''))).to.be.equal(true)
|
||||
|
||||
// When
|
||||
const { tx: secondTx } = await contract.requestFailedMessageFix(exampleTxHash)
|
||||
|
||||
// Then
|
||||
const secondReceipt = await web3.eth.getTransactionReceipt(secondTx)
|
||||
const secondLogs = AMBMock.decodeLogs(secondReceipt.logs)
|
||||
expect(secondLogs.length).to.be.equal(1)
|
||||
expect(secondLogs[0].args.encodedData.includes(dataHash.replace(/^0x/, ''))).to.be.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shouldBehaveLikeBasicMediator
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
const ForeignMediator = artifacts.require('ForeignMediator.sol')
|
||||
const HomeMediator = artifacts.require('HomeMediator.sol')
|
||||
const SimpleBridgeKitty = artifacts.require('SimpleBridgeKitty.sol')
|
||||
const AMBMock = artifacts.require('AMBMock.sol')
|
||||
|
||||
const { expectRevert, expectEvent, BN } = require('openzeppelin-test-helpers')
|
||||
const { expect } = require('chai')
|
||||
const { shouldBehaveLikeBasicMediator } = require('./basicMediator.test')
|
||||
|
||||
const {
|
||||
maxGasPerTx,
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
exampleTxHash,
|
||||
nonce
|
||||
} = require('./helpers')
|
||||
|
||||
contract('ForeignMediator', accounts => {
|
||||
const owner = accounts[0]
|
||||
const user = accounts[1]
|
||||
beforeEach(async function() {
|
||||
this.bridge = await ForeignMediator.new()
|
||||
this.mediatorContractOnOtherSide = HomeMediator
|
||||
})
|
||||
shouldBehaveLikeBasicMediator(accounts)
|
||||
describe('transferToken', () => {
|
||||
it('should transfer tokens to mediator and emit event on amb bridge ', async () => {
|
||||
// Given
|
||||
const contract = await ForeignMediator.new()
|
||||
const bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
const token = await SimpleBridgeKitty.new()
|
||||
const mediatorContractOnOtherSide = await HomeMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
await token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
// When
|
||||
// should approve the transfer first
|
||||
await expectRevert.unspecified(contract.transferToken(user, tokenId, { from: user }))
|
||||
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
|
||||
// Then
|
||||
await expectEvent.inTransaction(tx, SimpleBridgeKitty, 'Transfer', {
|
||||
from: user,
|
||||
to: contract.address,
|
||||
tokenId: new BN(tokenId)
|
||||
})
|
||||
await expectEvent.inTransaction(tx, AMBMock, 'MockedEvent')
|
||||
})
|
||||
})
|
||||
describe('handleBridgedTokens', () => {
|
||||
it('should transfer locked token', async () => {
|
||||
// Given
|
||||
const contract = await ForeignMediator.new()
|
||||
const bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
const token = await SimpleBridgeKitty.new()
|
||||
const mediatorContractOnOtherSide = await HomeMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
await token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
contract.address,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(contract.address)
|
||||
|
||||
// must be called from bridge
|
||||
await expectRevert.unspecified(contract.handleBridgedTokens(user, tokenId, nonce, { from: user }))
|
||||
await expectRevert.unspecified(contract.handleBridgedTokens(user, tokenId, nonce, { from: owner }))
|
||||
|
||||
const data = await contract.contract.methods.handleBridgedTokens(user, tokenId, nonce).encodeABI()
|
||||
|
||||
const failedTxHash = '0x2ebc2ccc755acc8eaf9252e19573af708d644ab63a39619adb080a3500a4ff2e'
|
||||
|
||||
// message must be generated by mediator contract on the other network
|
||||
await bridgeContract.executeMessageCall(contract.address, owner, data, failedTxHash, 1000000)
|
||||
expect(await bridgeContract.messageCallStatus(failedTxHash)).to.be.equal(false)
|
||||
|
||||
const { tx } = await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(true)
|
||||
|
||||
// Then
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
|
||||
await expectEvent.inTransaction(tx, SimpleBridgeKitty, 'Transfer', {
|
||||
from: contract.address,
|
||||
to: user,
|
||||
tokenId: new BN(tokenId)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('fixFailedMessage', () => {
|
||||
let bridgeContract
|
||||
let contract
|
||||
let mediatorContractOnOtherSide
|
||||
let token
|
||||
let dataHash
|
||||
beforeEach(async () => {
|
||||
bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
token = await SimpleBridgeKitty.new()
|
||||
|
||||
contract = await ForeignMediator.new()
|
||||
mediatorContractOnOtherSide = await HomeMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
// User has a token
|
||||
await token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
// User transfer token to mediator and generate amb event
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(contract.address)
|
||||
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
const data = `0x${logs[0].args.encodedData.substr(148, logs[0].args.encodedData.length - 148)}`
|
||||
|
||||
// Bridge calls mediator from other side
|
||||
await bridgeContract.executeMessageCall(contract.address, mediatorContractOnOtherSide.address, data, tx, 100)
|
||||
// Message failed
|
||||
expect(await bridgeContract.messageCallStatus(tx)).to.be.equal(false)
|
||||
|
||||
// mediator from other side should use this dataHash to request fix the failed message
|
||||
dataHash = await bridgeContract.failedMessageDataHash(tx)
|
||||
})
|
||||
it('should fix locked tokens', async () => {
|
||||
// Given
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
const fixData = await contract.contract.methods.fixFailedMessage(dataHash).encodeABI()
|
||||
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
fixData,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(true)
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(true)
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
|
||||
const otherTxHash = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
|
||||
// can only fix it one time
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
fixData,
|
||||
otherTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(otherTxHash)).to.be.equal(false)
|
||||
|
||||
// Re send token to know that dataHash is different even if same tokenId and metadata is used
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(contract.address)
|
||||
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
const data = `0x${logs[0].args.encodedData.substr(148, logs[0].args.encodedData.length - 148)}`
|
||||
|
||||
// Bridge calls mediator from other side
|
||||
await bridgeContract.executeMessageCall(contract.address, mediatorContractOnOtherSide.address, data, tx, 100)
|
||||
// Message failed
|
||||
expect(await bridgeContract.messageCallStatus(tx)).to.be.equal(false)
|
||||
|
||||
// mediator from other side should use this dataHash to request fix the failed message
|
||||
const newDataHash = await bridgeContract.failedMessageDataHash(tx)
|
||||
|
||||
expect(newDataHash).not.to.be.equal(dataHash)
|
||||
})
|
||||
it('should be called by bridge', async () => {
|
||||
await expectRevert.unspecified(contract.fixFailedMessage(dataHash, { from: owner }))
|
||||
})
|
||||
it('message sender should be mediator from other side ', async () => {
|
||||
// Given
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
const fixData = await contract.contract.methods.fixFailedMessage(dataHash).encodeABI()
|
||||
|
||||
await bridgeContract.executeMessageCall(contract.address, contract.address, fixData, exampleTxHash, 1000000)
|
||||
|
||||
// Then
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,43 @@
|
|||
const { ether } = require('openzeppelin-test-helpers')
|
||||
|
||||
const maxGasPerTx = ether('1')
|
||||
const tokenId = 1
|
||||
const isReady = true
|
||||
const cooldownIndex = 0
|
||||
const nextActionAt = 2
|
||||
const siringWithId = 3
|
||||
const birthTime = 1511417999
|
||||
const matronId = 4
|
||||
const sireId = 5
|
||||
const generation = 6
|
||||
const genes = '623494533466173608148163391622294272936404886827521876326676079749575115'
|
||||
const exampleTxHash = '0xf308b922ab9f8a7128d9d7bc9bce22cd88b2c05c8213f0e2d8104d78e0a9ecbb'
|
||||
const metadata =
|
||||
'0x' +
|
||||
'0000000000000000000000000000000000000000000000000000000000000001' + // isGestating
|
||||
'0000000000000000000000000000000000000000000000000000000000000001' + // isReady
|
||||
'0000000000000000000000000000000000000000000000000000000000000000' + // cooldownIndex
|
||||
'0000000000000000000000000000000000000000000000000000000000000002' + // nextActionAt
|
||||
'0000000000000000000000000000000000000000000000000000000000000003' + // siringWithId
|
||||
'000000000000000000000000000000000000000000000000000000005a16688f' + // birthTime
|
||||
'0000000000000000000000000000000000000000000000000000000000000004' + // matronId
|
||||
'0000000000000000000000000000000000000000000000000000000000000005' + // sireId
|
||||
'0000000000000000000000000000000000000000000000000000000000000006' + // generation
|
||||
'00005a56b294e64a52e421c928c63218845adac30406314c739c454bd2e731cb' // genes
|
||||
const nonce = '0x96b6af865cdaa107ede916e237afbedffa5ed36bea84c0e77a33cc28fc2e9c01'
|
||||
module.exports = {
|
||||
maxGasPerTx,
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
metadata,
|
||||
exampleTxHash,
|
||||
nonce
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
const HomeMediator = artifacts.require('HomeMediator.sol')
|
||||
const ForeignMediator = artifacts.require('ForeignMediator.sol')
|
||||
const SimpleBridgeKitty = artifacts.require('SimpleBridgeKitty.sol')
|
||||
const AMBMock = artifacts.require('AMBMock.sol')
|
||||
|
||||
const { expectRevert, expectEvent, BN } = require('openzeppelin-test-helpers')
|
||||
const { expect } = require('chai')
|
||||
const { shouldBehaveLikeBasicMediator } = require('./basicMediator.test')
|
||||
|
||||
const {
|
||||
maxGasPerTx,
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
metadata,
|
||||
exampleTxHash,
|
||||
nonce
|
||||
} = require('./helpers')
|
||||
|
||||
contract('HomeMediator', accounts => {
|
||||
const owner = accounts[0]
|
||||
const user = accounts[1]
|
||||
beforeEach(async function() {
|
||||
this.bridge = await HomeMediator.new()
|
||||
this.mediatorContractOnOtherSide = ForeignMediator
|
||||
})
|
||||
shouldBehaveLikeBasicMediator(accounts)
|
||||
describe('transferToken', () => {
|
||||
it('should transfer token to mediator, burn the token and emit event on amb bridge ', async () => {
|
||||
// Given
|
||||
const contract = await HomeMediator.new()
|
||||
const bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
const token = await SimpleBridgeKitty.new()
|
||||
const mediatorContractOnOtherSide = await ForeignMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
await token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
await token.transferBridgeRole(contract.address, { from: owner })
|
||||
|
||||
// When
|
||||
// should approve the transfer first
|
||||
await expectRevert.unspecified(contract.transferToken(user, tokenId, { from: user }))
|
||||
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
|
||||
// Then
|
||||
await expectEvent.inTransaction(tx, SimpleBridgeKitty, 'Transfer', {
|
||||
from: user,
|
||||
to: contract.address,
|
||||
tokenId: new BN(tokenId)
|
||||
})
|
||||
await expectEvent.inTransaction(tx, SimpleBridgeKitty, 'Death', {
|
||||
kittyId: new BN(tokenId)
|
||||
})
|
||||
await expectEvent.inTransaction(tx, AMBMock, 'MockedEvent')
|
||||
})
|
||||
})
|
||||
describe('handleBridgedTokens', () => {
|
||||
it('should mint with token Id and metadata', async () => {
|
||||
// Given
|
||||
const contract = await HomeMediator.new()
|
||||
const bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
const token = await SimpleBridgeKitty.new()
|
||||
const mediatorContractOnOtherSide = await ForeignMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
await token.transferBridgeRole(contract.address, { from: owner })
|
||||
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('0')
|
||||
|
||||
// When
|
||||
|
||||
// must be called from bridge
|
||||
await expectRevert.unspecified(contract.handleBridgedTokens(user, tokenId, metadata, nonce, { from: user }))
|
||||
await expectRevert.unspecified(contract.handleBridgedTokens(user, tokenId, metadata, nonce, { from: owner }))
|
||||
|
||||
const data = await contract.contract.methods.handleBridgedTokens(user, tokenId, metadata, nonce).encodeABI()
|
||||
|
||||
const failedTxHash = '0x2ebc2ccc755acc8eaf9252e19573af708d644ab63a39619adb080a3500a4ff2e'
|
||||
|
||||
// message must be generated by mediator contract on the other network
|
||||
await bridgeContract.executeMessageCall(contract.address, owner, data, failedTxHash, 1000000)
|
||||
expect(await bridgeContract.messageCallStatus(failedTxHash)).to.be.equal(false)
|
||||
|
||||
const { tx } = await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
data,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(true)
|
||||
|
||||
// Then
|
||||
await expectEvent.inTransaction(tx, SimpleBridgeKitty, 'Birth', {
|
||||
owner: user,
|
||||
kittyId: new BN(tokenId),
|
||||
matronId: new BN(matronId),
|
||||
sireId: new BN(sireId),
|
||||
genes
|
||||
})
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
const tokenList = await token.tokensOfOwner(user)
|
||||
expect(tokenList.length).to.be.equal(1)
|
||||
expect(tokenList[0]).to.be.bignumber.equal(new BN(tokenId))
|
||||
|
||||
const mintedKitty = await token.getKitty(tokenId)
|
||||
|
||||
expect(mintedKitty.isGestating).to.be.equal(true)
|
||||
expect(mintedKitty.isReady).to.be.equal(isReady)
|
||||
expect(mintedKitty.cooldownIndex).to.be.bignumber.equal(new BN(cooldownIndex))
|
||||
expect(mintedKitty.nextActionAt).to.be.bignumber.equal(new BN(nextActionAt))
|
||||
expect(mintedKitty.siringWithId).to.be.bignumber.equal(new BN(siringWithId))
|
||||
expect(mintedKitty.birthTime).to.be.bignumber.equal(new BN(birthTime))
|
||||
expect(mintedKitty.matronId).to.be.bignumber.equal(new BN(matronId))
|
||||
expect(mintedKitty.sireId).to.be.bignumber.equal(new BN(sireId))
|
||||
expect(mintedKitty.generation).to.be.bignumber.equal(new BN(generation))
|
||||
expect(mintedKitty.genes).to.be.bignumber.equal(new BN(genes))
|
||||
})
|
||||
})
|
||||
describe('fixFailedMessage', () => {
|
||||
let bridgeContract
|
||||
let contract
|
||||
let mediatorContractOnOtherSide
|
||||
let token
|
||||
let dataHash
|
||||
beforeEach(async () => {
|
||||
bridgeContract = await AMBMock.new()
|
||||
await bridgeContract.setMaxGasPerTx(maxGasPerTx)
|
||||
token = await SimpleBridgeKitty.new()
|
||||
|
||||
contract = await HomeMediator.new()
|
||||
mediatorContractOnOtherSide = await ForeignMediator.new()
|
||||
|
||||
await contract.initialize(
|
||||
bridgeContract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
token.address,
|
||||
maxGasPerTx,
|
||||
owner
|
||||
)
|
||||
|
||||
// User has a token
|
||||
await token.mint(
|
||||
tokenId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
await token.transferBridgeRole(contract.address, { from: owner })
|
||||
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
// User transfer token to mediator, it burns the token and generate amb event
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
await expectRevert.unspecified(token.ownerOf(tokenId))
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('0')
|
||||
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
const data = `0x${logs[0].args.encodedData.substr(148, logs[0].args.encodedData.length - 148)}`
|
||||
|
||||
// Bridge calls mediator from other side
|
||||
await bridgeContract.executeMessageCall(contract.address, mediatorContractOnOtherSide.address, data, tx, 100)
|
||||
// Message failed
|
||||
expect(await bridgeContract.messageCallStatus(tx)).to.be.equal(false)
|
||||
|
||||
// mediator from other side should use this dataHash to request fix the failed message
|
||||
dataHash = await bridgeContract.failedMessageDataHash(tx)
|
||||
})
|
||||
it('should fix burnt tokens', async () => {
|
||||
// Given
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
const fixData = await contract.contract.methods.fixFailedMessage(dataHash).encodeABI()
|
||||
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
fixData,
|
||||
exampleTxHash,
|
||||
1000000
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(true)
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(true)
|
||||
expect(await token.ownerOf(tokenId)).to.be.equal(user)
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
const mintedKitty = await token.getKitty(tokenId)
|
||||
|
||||
expect(mintedKitty.isGestating).to.be.equal(true)
|
||||
expect(mintedKitty.isReady).to.be.equal(isReady)
|
||||
expect(mintedKitty.cooldownIndex).to.be.bignumber.equal(new BN(cooldownIndex))
|
||||
expect(mintedKitty.nextActionAt).to.be.bignumber.equal(new BN(nextActionAt))
|
||||
expect(mintedKitty.siringWithId).to.be.bignumber.equal(new BN(siringWithId))
|
||||
expect(mintedKitty.birthTime).to.be.bignumber.equal(new BN(birthTime))
|
||||
expect(mintedKitty.matronId).to.be.bignumber.equal(new BN(matronId))
|
||||
expect(mintedKitty.sireId).to.be.bignumber.equal(new BN(sireId))
|
||||
expect(mintedKitty.generation).to.be.bignumber.equal(new BN(generation))
|
||||
expect(mintedKitty.genes).to.be.bignumber.equal(new BN(genes))
|
||||
|
||||
const otherTxHash = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
|
||||
// can only fix it one time
|
||||
await bridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
mediatorContractOnOtherSide.address,
|
||||
fixData,
|
||||
otherTxHash,
|
||||
1000000
|
||||
)
|
||||
expect(await bridgeContract.messageCallStatus(otherTxHash)).to.be.equal(false)
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
|
||||
// Re send token to know that dataHash is different even if same tokenId and metadata is used
|
||||
await token.approve(contract.address, tokenId, { from: user })
|
||||
const { tx } = await contract.transferToken(user, tokenId, { from: user })
|
||||
await expectRevert.unspecified(token.ownerOf(tokenId))
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('0')
|
||||
|
||||
const receipt = await web3.eth.getTransactionReceipt(tx)
|
||||
const logs = AMBMock.decodeLogs(receipt.logs)
|
||||
const data = `0x${logs[0].args.encodedData.substr(148, logs[0].args.encodedData.length - 148)}`
|
||||
|
||||
// Bridge calls mediator from other side
|
||||
await bridgeContract.executeMessageCall(contract.address, mediatorContractOnOtherSide.address, data, tx, 100)
|
||||
// Message failed
|
||||
expect(await bridgeContract.messageCallStatus(tx)).to.be.equal(false)
|
||||
|
||||
// mediator from other side should use this dataHash to request fix the failed message
|
||||
const newDataHash = await bridgeContract.failedMessageDataHash(tx)
|
||||
|
||||
expect(newDataHash).not.to.be.equal(dataHash)
|
||||
})
|
||||
it('should be called by bridge', async () => {
|
||||
await expectRevert.unspecified(contract.fixFailedMessage(dataHash, { from: owner }))
|
||||
})
|
||||
it('message sender should be mediator from other side ', async () => {
|
||||
// Given
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
|
||||
// When
|
||||
const fixData = await contract.contract.methods.fixFailedMessage(dataHash).encodeABI()
|
||||
|
||||
await bridgeContract.executeMessageCall(contract.address, contract.address, fixData, exampleTxHash, 1000000)
|
||||
|
||||
// Then
|
||||
expect(await bridgeContract.messageCallStatus(exampleTxHash)).to.be.equal(false)
|
||||
expect(await contract.messageHashFixed(dataHash)).to.be.equal(false)
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('0')
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,387 @@
|
|||
const SimpleBridgeKitty = artifacts.require('SimpleBridgeKitty.sol')
|
||||
|
||||
const { expectRevert, expectEvent, BN, constants } = require('openzeppelin-test-helpers')
|
||||
const { expect } = require('chai')
|
||||
|
||||
const kittyId = 1
|
||||
const isReady = true
|
||||
const cooldownIndex = 0
|
||||
const nextActionAt = 2
|
||||
const siringWithId = 3
|
||||
const birthTime = 1511417999
|
||||
const matronId = 4
|
||||
const sireId = 5
|
||||
const generation = 6
|
||||
const genes = '623494533466173608148163391622294272936404886827521876326676079749575115'
|
||||
|
||||
contract('SimpleBridgeKitty', accounts => {
|
||||
let token
|
||||
const owner = accounts[0]
|
||||
const user = accounts[1]
|
||||
|
||||
beforeEach(async () => {
|
||||
token = await SimpleBridgeKitty.new()
|
||||
})
|
||||
describe('mint', () => {
|
||||
it('should mint with token Id and metadata', async () => {
|
||||
// Given
|
||||
expect(await token.bridge()).to.be.equal(owner)
|
||||
|
||||
// When
|
||||
// only owner can mint
|
||||
await expectRevert.unspecified(
|
||||
token.mint(
|
||||
kittyId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: user }
|
||||
)
|
||||
)
|
||||
|
||||
const { logs } = await token.mint(
|
||||
kittyId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
// Then
|
||||
await expectEvent.inLogs(logs, 'Birth', {
|
||||
owner: user,
|
||||
kittyId: new BN(kittyId),
|
||||
matronId: new BN(matronId),
|
||||
sireId: new BN(sireId),
|
||||
genes: new BN(genes)
|
||||
})
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
const tokenList = await token.tokensOfOwner(user)
|
||||
expect(tokenList.length).to.be.equal(1)
|
||||
expect(tokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
})
|
||||
})
|
||||
describe('getKitty', () => {
|
||||
it('should return metadata of kitty', async () => {
|
||||
// Given
|
||||
await token.mint(
|
||||
kittyId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
// When
|
||||
const metadata = await token.getKitty(kittyId)
|
||||
|
||||
// Then
|
||||
expect(metadata.isGestating).to.be.equal(true)
|
||||
expect(metadata.isReady).to.be.equal(true)
|
||||
expect(metadata.cooldownIndex).to.be.bignumber.equal(new BN(cooldownIndex))
|
||||
expect(metadata.nextActionAt).to.be.bignumber.equal(new BN(nextActionAt))
|
||||
expect(metadata.siringWithId).to.be.bignumber.equal(new BN(siringWithId))
|
||||
expect(metadata.birthTime).to.be.bignumber.equal(new BN(birthTime))
|
||||
expect(metadata.matronId).to.be.bignumber.equal(new BN(matronId))
|
||||
expect(metadata.sireId).to.be.bignumber.equal(new BN(sireId))
|
||||
expect(metadata.generation).to.be.bignumber.equal(new BN(generation))
|
||||
expect(metadata.genes).to.be.bignumber.equal(new BN(genes))
|
||||
})
|
||||
})
|
||||
describe('burn', () => {
|
||||
it('should burn the kitty', async () => {
|
||||
// Given
|
||||
await token.mint(
|
||||
kittyId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
// Then
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
const initialUserTokenList = await token.tokensOfOwner(user)
|
||||
expect(initialUserTokenList.length).to.be.equal(1)
|
||||
expect(initialUserTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
// When
|
||||
|
||||
// only owner of contract can burn
|
||||
await expectRevert.unspecified(token.burn(kittyId, { from: user }))
|
||||
// only owner of kitty can burn
|
||||
await expectRevert.unspecified(token.burn(kittyId, { from: owner }))
|
||||
|
||||
await token.transfer(owner, kittyId, { from: user })
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(owner)
|
||||
const userTokenList = await token.tokensOfOwner(user)
|
||||
expect(userTokenList.length).to.be.equal(0)
|
||||
const ownerTokenList = await token.tokensOfOwner(owner)
|
||||
expect(ownerTokenList.length).to.be.equal(1)
|
||||
expect(ownerTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
const { logs } = await token.burn(kittyId, { from: owner })
|
||||
|
||||
// Then
|
||||
await expectEvent.inLogs(logs, 'Death', {
|
||||
kittyId: new BN(kittyId)
|
||||
})
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('0')
|
||||
await expectRevert.unspecified(token.ownerOf(kittyId))
|
||||
const tokenList = await token.tokensOfOwner(owner)
|
||||
expect(tokenList.length).to.be.equal(0)
|
||||
|
||||
const metadata = await token.getKitty(kittyId)
|
||||
|
||||
expect(metadata.isGestating).to.be.equal(false)
|
||||
expect(metadata.isReady).to.be.equal(false)
|
||||
expect(metadata.cooldownIndex).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.nextActionAt).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.siringWithId).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.birthTime).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.matronId).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.sireId).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.generation).to.be.bignumber.equal(new BN(0))
|
||||
expect(metadata.genes).to.be.bignumber.equal(new BN(0))
|
||||
})
|
||||
})
|
||||
describe('ERC721', () => {
|
||||
beforeEach(async () => {
|
||||
token = await SimpleBridgeKitty.new()
|
||||
|
||||
await token.mint(
|
||||
kittyId,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
})
|
||||
describe('metadata', () => {
|
||||
it('should return name', async () => {
|
||||
expect(await token.name()).to.be.equal('CryptoKitties')
|
||||
})
|
||||
it('should return symbol', async () => {
|
||||
expect(await token.symbol()).to.be.equal('CK')
|
||||
})
|
||||
})
|
||||
describe('totalSupply', () => {
|
||||
it('should return total supply', async () => {
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('1')
|
||||
|
||||
await token.mint(
|
||||
2,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
owner,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
await token.mint(
|
||||
3,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
user,
|
||||
{ from: owner }
|
||||
)
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('3')
|
||||
|
||||
await token.burn(2, { from: owner })
|
||||
expect(await token.totalSupply()).to.be.bignumber.equal('2')
|
||||
})
|
||||
})
|
||||
describe('balanceOf', () => {
|
||||
it('should return amount of tokens of a user', async () => {
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal('0')
|
||||
|
||||
await token.mint(
|
||||
2,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
owner,
|
||||
{ from: owner }
|
||||
)
|
||||
|
||||
await token.mint(
|
||||
3,
|
||||
isReady,
|
||||
cooldownIndex,
|
||||
nextActionAt,
|
||||
siringWithId,
|
||||
birthTime,
|
||||
matronId,
|
||||
sireId,
|
||||
generation,
|
||||
genes,
|
||||
owner,
|
||||
{ from: owner }
|
||||
)
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal('2')
|
||||
|
||||
await token.burn(2, { from: owner })
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal('1')
|
||||
})
|
||||
})
|
||||
describe('ownerOf', () => {
|
||||
it('should return owner of token', async () => {
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
|
||||
await token.transfer(owner, kittyId, { from: user })
|
||||
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(owner)
|
||||
})
|
||||
})
|
||||
describe('approve', () => {
|
||||
it('should emit an approval event', async () => {
|
||||
// Given
|
||||
const user2 = accounts[2]
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
|
||||
// When
|
||||
|
||||
// should own the token
|
||||
await expectRevert.unspecified(token.approve(user2, kittyId, { from: owner }))
|
||||
|
||||
const { logs } = await token.approve(user2, kittyId, { from: user })
|
||||
|
||||
// Then
|
||||
await expectEvent.inLogs(logs, 'Approval', {
|
||||
owner: user,
|
||||
approved: user2,
|
||||
tokenId: new BN(kittyId)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('transfer', () => {
|
||||
it('should transfer ownership of the token', async () => {
|
||||
// Given
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal('1')
|
||||
const initialUserTokenList = await token.tokensOfOwner(user)
|
||||
expect(initialUserTokenList.length).to.be.equal(1)
|
||||
expect(initialUserTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
// When
|
||||
// should own the token
|
||||
await expectRevert.unspecified(token.transfer(owner, kittyId, { from: owner }))
|
||||
// can't transfer to zero address
|
||||
await expectRevert.unspecified(token.transfer(constants.ZERO_ADDRESS, kittyId, { from: user }))
|
||||
// can't transfer to token address
|
||||
await expectRevert.unspecified(token.transfer(token.address, kittyId, { from: user }))
|
||||
const { logs } = await token.transfer(owner, kittyId, { from: user })
|
||||
|
||||
// Then
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(owner)
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal('0')
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal('1')
|
||||
const userTokenList = await token.tokensOfOwner(user)
|
||||
expect(userTokenList.length).to.be.equal(0)
|
||||
const ownerTokenList = await token.tokensOfOwner(owner)
|
||||
expect(ownerTokenList.length).to.be.equal(1)
|
||||
expect(ownerTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
await expectEvent.inLogs(logs, 'Transfer', {
|
||||
from: user,
|
||||
to: owner,
|
||||
tokenId: new BN(kittyId)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('transferFrom', () => {
|
||||
it('should transfer ownership of a previously approved token', async () => {
|
||||
// Given
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(user)
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal('1')
|
||||
const initialUserTokenList = await token.tokensOfOwner(user)
|
||||
expect(initialUserTokenList.length).to.be.equal(1)
|
||||
expect(initialUserTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
// When
|
||||
// should first approve the transfer
|
||||
await expectRevert.unspecified(token.transferFrom(user, owner, kittyId, { from: owner }))
|
||||
|
||||
await token.approve(owner, kittyId, { from: user })
|
||||
|
||||
// can't transfer to zero address
|
||||
await expectRevert.unspecified(token.transferFrom(user, constants.ZERO_ADDRESS, kittyId, { from: owner }))
|
||||
// can't transfer to token address
|
||||
await expectRevert.unspecified(token.transferFrom(user, token.address, kittyId, { from: owner }))
|
||||
const { logs } = await token.transferFrom(user, owner, kittyId, { from: owner })
|
||||
|
||||
// Then
|
||||
expect(await token.ownerOf(kittyId)).to.be.equal(owner)
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal('0')
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal('1')
|
||||
const userTokenList = await token.tokensOfOwner(user)
|
||||
expect(userTokenList.length).to.be.equal(0)
|
||||
const ownerTokenList = await token.tokensOfOwner(owner)
|
||||
expect(ownerTokenList.length).to.be.equal(1)
|
||||
expect(ownerTokenList[0]).to.be.bignumber.equal(new BN(kittyId))
|
||||
|
||||
await expectEvent.inLogs(logs, 'Transfer', {
|
||||
from: user,
|
||||
to: owner,
|
||||
tokenId: new BN(kittyId)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
171
yarn.lock
171
yarn.lock
|
@ -64,6 +64,25 @@
|
|||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@truffle/blockchain-utils@^0.0.11":
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.11.tgz#9886f4cb7a9f20deded4451ac78f8567ae5c0d75"
|
||||
integrity sha512-9MyQ/20M96clhIcC7fVFIckGSB8qMsmcdU6iYt98HXJ9GOLNKsCaJFz1OVsJncVreYwTUhoEXTrVBc8zrmPDJQ==
|
||||
|
||||
"@truffle/contract-schema@^3.0.14":
|
||||
version "3.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.0.16.tgz#0de8670b2b0dacba57b43b65911c5376dd00bbdf"
|
||||
integrity sha512-E88YTZNVvnSprvKS8qMx7e5zm3VAgkCAciQrX1VC+h14EbCJxHyg/9iBVm26ySKgghRzV3Ia8Y6ZJQt6o2iSJw==
|
||||
dependencies:
|
||||
ajv "^6.10.0"
|
||||
crypto-js "^3.1.9-1"
|
||||
debug "^4.1.0"
|
||||
|
||||
"@truffle/error@^0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.6.tgz#75d499845b4b3a40537889e7d04c663afcaee85d"
|
||||
integrity sha512-QUM9ZWiwlXGixFGpV18g5I6vua6/r+ZV9W/5DQA5go9A3eZUNPHPaTKMIQPJLYn6+ZV5jg5H28zCHq56LHF3yA==
|
||||
|
||||
"@types/node@^10.3.2":
|
||||
version "10.14.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.18.tgz#b7d45fc950e6ffd7edc685e890d13aa7b8535dce"
|
||||
|
@ -107,6 +126,11 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5, ajv@^6.6.1, ajv@^6.9.1:
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-colors@^3.2.3:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
|
||||
integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
|
||||
|
||||
ansi-escapes@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
|
||||
|
@ -185,6 +209,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||
|
||||
assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
|
||||
|
||||
astral-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
|
||||
|
@ -227,6 +256,11 @@ bcrypt-pbkdf@^1.0.0:
|
|||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
bignumber.js@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
|
||||
integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==
|
||||
|
||||
bignumber.js@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075"
|
||||
|
@ -451,6 +485,23 @@ caseless@~0.12.0:
|
|||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
chai-bn@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chai-bn/-/chai-bn-0.1.1.tgz#a8904b2dc878e5c094881f327c0029579ff2062b"
|
||||
integrity sha512-e1npVXt3cQfZ6oQET9oP38vNj/4HeJ4ojeUpuC8YzhVbTJpIDqANVt7TKi7Dq9yKlHySk2FqbmiMih35iT4DYg==
|
||||
|
||||
chai@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
|
||||
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
|
||||
dependencies:
|
||||
assertion-error "^1.1.0"
|
||||
check-error "^1.0.2"
|
||||
deep-eql "^3.0.1"
|
||||
get-func-name "^2.0.0"
|
||||
pathval "^1.1.0"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
|
@ -465,6 +516,11 @@ chardet@^0.7.0:
|
|||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
check-error@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6"
|
||||
|
@ -657,6 +713,11 @@ crypto-browserify@3.12.0:
|
|||
randombytes "^2.0.0"
|
||||
randomfill "^1.0.3"
|
||||
|
||||
crypto-js@^3.1.9-1:
|
||||
version "3.1.9-1"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.1.9-1.tgz#fda19e761fc077e01ffbfdc6e9fdfc59e8806cd8"
|
||||
integrity sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=
|
||||
|
||||
d@1, d@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
|
||||
|
@ -693,7 +754,7 @@ debug@^3.1.0:
|
|||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.0.1:
|
||||
debug@^4.0.1, debug@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||
|
@ -765,6 +826,13 @@ decompress@^4.0.0:
|
|||
pify "^2.3.0"
|
||||
strip-dirs "^2.0.0"
|
||||
|
||||
deep-eql@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
|
||||
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
|
@ -1314,6 +1382,31 @@ ethers@4.0.0-beta.3:
|
|||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethers@^4.0.0-beta.1, ethers@^4.0.32:
|
||||
version "4.0.37"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.37.tgz#dfa70d59498663878c5e4a977d14356660ca5b90"
|
||||
integrity sha512-B7bDdyQ45A5lPr6k2HOkEKMtYOuqlfy+nNf8glnRvWidkDQnToKw1bv7UyrwlbsIgY2mE03UxTVtouXcT6Vvcw==
|
||||
dependencies:
|
||||
"@types/node" "^10.3.2"
|
||||
aes-js "3.0.0"
|
||||
bn.js "^4.4.0"
|
||||
elliptic "6.3.3"
|
||||
hash.js "1.1.3"
|
||||
js-sha3 "0.5.7"
|
||||
scrypt-js "2.0.4"
|
||||
setimmediate "1.0.4"
|
||||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethjs-abi@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.1.tgz#e0a7a93a7e81163a94477bad56ede524ab6de533"
|
||||
integrity sha1-4KepOn6BFjqUR3utVu3lJKtt5TM=
|
||||
dependencies:
|
||||
bn.js "4.11.6"
|
||||
js-sha3 "0.5.5"
|
||||
number-to-bn "1.7.0"
|
||||
|
||||
ethjs-unit@0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
|
||||
|
@ -1573,6 +1666,11 @@ functional-red-black-tree@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
|
||||
|
||||
get-stdin@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
|
||||
|
@ -2050,6 +2148,11 @@ isurl@^1.0.0-alpha5:
|
|||
has-to-string-tag-x "^1.2.0"
|
||||
is-object "^1.0.1"
|
||||
|
||||
js-sha3@0.5.5:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a"
|
||||
integrity sha1-uvDA6MVK1ZA0R9+Wreekobynmko=
|
||||
|
||||
js-sha3@0.5.7, js-sha3@^0.5.7:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
|
||||
|
@ -2176,7 +2279,7 @@ locate-path@^2.0.0:
|
|||
p-locate "^2.0.0"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14:
|
||||
lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
|
@ -2498,6 +2601,23 @@ onetime@^2.0.0:
|
|||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
openzeppelin-solidity@1.12.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-1.12.0.tgz#7b9c55975e73370d4541e3442b30cb3d91ac973a"
|
||||
integrity sha512-WlorzMXIIurugiSdw121RVD5qA3EfSI7GybTn+/Du0mPNgairjt29NpVTAaH8eLjAeAwlw46y7uQKy0NYem/gA==
|
||||
|
||||
openzeppelin-test-helpers@^0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/openzeppelin-test-helpers/-/openzeppelin-test-helpers-0.4.3.tgz#f1eaa3df6d31f12d7a24b5b35c23367376058db6"
|
||||
integrity sha512-ln5Exl5fciLIKIUZ6yX6Q2kO/XMQLhNh8G/uEcVfIPZCl/Q0z64ayqZZmsQLGJx1HpSZS27XjC5d48PjX72eEw==
|
||||
dependencies:
|
||||
ansi-colors "^3.2.3"
|
||||
chai-bn "^0.1.1"
|
||||
ethjs-abi "^0.2.1"
|
||||
semver "^5.6.0"
|
||||
truffle-contract "^4.0.26"
|
||||
web3-utils "^1.2.0"
|
||||
|
||||
optionator@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
|
||||
|
@ -2650,6 +2770,11 @@ path-type@^2.0.0:
|
|||
dependencies:
|
||||
pify "^2.0.0"
|
||||
|
||||
pathval@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
|
||||
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||
|
@ -3013,6 +3138,11 @@ scrypt-js@2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4"
|
||||
integrity sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=
|
||||
|
||||
scrypt-js@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16"
|
||||
integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==
|
||||
|
||||
scryptsy@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790"
|
||||
|
@ -3039,7 +3169,7 @@ seek-bzip@^1.0.5:
|
|||
dependencies:
|
||||
commander "~2.8.1"
|
||||
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1:
|
||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
@ -3466,6 +3596,22 @@ tough-cookie@~2.4.3:
|
|||
psl "^1.1.24"
|
||||
punycode "^1.4.1"
|
||||
|
||||
truffle-contract@^4.0.26:
|
||||
version "4.0.31"
|
||||
resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-4.0.31.tgz#e43b7f648e2db352c857d1202d710029b107b68d"
|
||||
integrity sha512-u3q+p1wiX5C2GpnluGx/d2iaJk7bcWshk2/TohiJyA2iQiTfkS7M4n9D9tY3JqpXR8PmD/TrA69RylO0RhITFA==
|
||||
dependencies:
|
||||
"@truffle/blockchain-utils" "^0.0.11"
|
||||
"@truffle/contract-schema" "^3.0.14"
|
||||
"@truffle/error" "^0.0.6"
|
||||
bignumber.js "^7.2.1"
|
||||
ethers "^4.0.0-beta.1"
|
||||
truffle-interface-adapter "^0.2.5"
|
||||
web3 "1.2.1"
|
||||
web3-core-promievent "1.2.1"
|
||||
web3-eth-abi "1.2.1"
|
||||
web3-utils "1.2.1"
|
||||
|
||||
truffle-flattener@^1.4.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.4.2.tgz#7460d0eec88ac67b150e8de3476f55d4420a4ba0"
|
||||
|
@ -3477,6 +3623,16 @@ truffle-flattener@^1.4.2:
|
|||
solidity-parser-antlr "^0.4.11"
|
||||
tsort "0.0.1"
|
||||
|
||||
truffle-interface-adapter@^0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/truffle-interface-adapter/-/truffle-interface-adapter-0.2.5.tgz#aa0bee635517b4a8e06adcdc99eacb993e68c243"
|
||||
integrity sha512-EL39OpP8FcZ99ne1Rno3jImfb92Nectd4iVsZzoEUCBfbwHe7sr0k+i45guoruSoP8nMUE81Mov2s8I5pi6d9Q==
|
||||
dependencies:
|
||||
bn.js "^4.11.8"
|
||||
ethers "^4.0.32"
|
||||
lodash "^4.17.13"
|
||||
web3 "1.2.1"
|
||||
|
||||
truffle@^5.0.35:
|
||||
version "5.0.35"
|
||||
resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.35.tgz#5c93522c3a0915567b2e5c7811934e0ee6e8a2bd"
|
||||
|
@ -3515,6 +3671,11 @@ type-check@~0.3.2:
|
|||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
type-is@~1.6.17, type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
@ -3855,7 +4016,7 @@ web3-shh@1.2.1:
|
|||
web3-core-subscriptions "1.2.1"
|
||||
web3-net "1.2.1"
|
||||
|
||||
web3-utils@1.2.1, web3-utils@^1.2.1:
|
||||
web3-utils@1.2.1, web3-utils@^1.2.0, web3-utils@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.1.tgz#21466e38291551de0ab34558de21512ac4274534"
|
||||
integrity sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==
|
||||
|
@ -3868,7 +4029,7 @@ web3-utils@1.2.1, web3-utils@^1.2.1:
|
|||
underscore "1.9.1"
|
||||
utf8 "3.0.0"
|
||||
|
||||
web3@^1.2.1:
|
||||
web3@1.2.1, web3@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.1.tgz#5d8158bcca47838ab8c2b784a2dee4c3ceb4179b"
|
||||
integrity sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==
|
||||
|
|
Loading…
Reference in New Issue