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:
Gerardo Nardelli 2019-09-27 14:32:54 -03:00 committed by Alexander Kolotov
parent af0bcb6196
commit 138dd3932c
34 changed files with 2278 additions and 40 deletions

View File

@ -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

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
bridge-contracts

View File

@ -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"
}
}

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "bridge-contracts"]
path = bridge-contracts
url = https://github.com/poanetwork/poa-bridge-contracts.git

View File

@ -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"]
}
}

View File

@ -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

1
bridge-contracts Submodule

@ -0,0 +1 @@
Subproject commit 86b35f8382d5cb98e5738128a0c1a3718c471c72

View File

@ -0,0 +1,5 @@
pragma solidity 0.4.24;
interface IForeignMediator {
function handleBridgedTokens(address _recipient, uint256 _tokenId, bytes32 _nonce) external;
}

View File

@ -0,0 +1,5 @@
pragma solidity 0.4.24;
interface IHomeMediator {
function handleBridgedTokens(address _recipient, uint256 _tokenId, bytes _metadata, bytes32 _nonce) external;
}

View File

@ -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;
}

View File

@ -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];
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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 {}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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}`)
}

117
deploy/initialize.js Normal file
View File

@ -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

View File

@ -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",

View File

@ -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

315
test/basicMediator.test.js Normal file
View File

@ -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
}

View File

@ -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)
})
})
})

43
test/helpers.js Normal file
View File

@ -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
}

303
test/homeMediator.test.js Normal file
View File

@ -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')
})
})
})

View File

@ -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
View File

@ -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==