Merge pull request #2323 from 0xProject/feat/3.0-staking-ganache
ganache: Added Staking, Forwarder, ERC20Bridge
This commit is contained in:
commit
58276cefce
@ -262,11 +262,11 @@ export class StakingContract extends BaseContract {
|
|||||||
{
|
{
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
name: 'wethAddress',
|
name: 'wethAddress_',
|
||||||
type: 'address',
|
type: 'address',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'zrxVaultAddress',
|
name: 'zrxVaultAddress_',
|
||||||
type: 'address',
|
type: 'address',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -101,17 +101,17 @@
|
|||||||
"exchange": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
|
"exchange": "0x48bacb9266a570d521063ef5dd96e61686dbe788",
|
||||||
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
|
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
|
||||||
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
|
||||||
"forwarder": "0x0000000000000000000000000000000000000000",
|
"forwarder": "0xaa86dda78e9434aca114b6676fc742a18d15a1cc",
|
||||||
"orderValidator": "0x0000000000000000000000000000000000000000",
|
"orderValidator": "0x0000000000000000000000000000000000000000",
|
||||||
"dutchAuction": "0x0000000000000000000000000000000000000000",
|
"dutchAuction": "0x0000000000000000000000000000000000000000",
|
||||||
"coordinatorRegistry": "0x1941ff73d1154774d87521d2d0aaad5d19c8df60",
|
"coordinatorRegistry": "0x1941ff73d1154774d87521d2d0aaad5d19c8df60",
|
||||||
"coordinator": "0x0000000000000000000000000000000000000000",
|
"coordinator": "0x0d8b0dd11f5d34ed41d556def5f841900d5b1c6b",
|
||||||
"multiAssetProxy": "0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db",
|
"multiAssetProxy": "0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db",
|
||||||
"staticCallProxy": "0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f",
|
"staticCallProxy": "0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f",
|
||||||
"devUtils": "0x38ef19fdf8e8415f18c307ed71967e19aac28ba1",
|
"devUtils": "0x38ef19fdf8e8415f18c307ed71967e19aac28ba1",
|
||||||
"zrxVault": "0x0000000000000000000000000000000000000000",
|
"zrxVault": "0x609acc8b356894a937fc58f3411f9528de96ecb1",
|
||||||
"staking": "0x0000000000000000000000000000000000000000",
|
"staking": "0xca9717a4a6e8009b3518648c9f3e7676255471a1",
|
||||||
"stakingProxy": "0x0000000000000000000000000000000000000000",
|
"stakingProxy": "0x4586649629f699f9a4b61d0e962dc3c9025fe488",
|
||||||
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000"
|
"erc20BridgeProxy": "0xf23276778860e420acfc18ebeebf7e829b06965c"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Replace Staking artifact with TestStaking",
|
"note": "Replace Staking artifact with TestStaking",
|
||||||
"pr": 2322
|
"pr": 2322
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `ZrxVault` and `ERC20BridgeProxy` artifacts",
|
||||||
|
"pr": 2323
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
218
packages/contract-artifacts/artifacts/ERC20BridgeProxy.json
generated
Normal file
218
packages/contract-artifacts/artifacts/ERC20BridgeProxy.json
generated
Normal file
File diff suppressed because one or more lines are too long
372
packages/contract-artifacts/artifacts/ZrxVault.json
generated
Normal file
372
packages/contract-artifacts/artifacts/ZrxVault.json
generated
Normal file
File diff suppressed because one or more lines are too long
@ -23,6 +23,8 @@ import * as StakingProxy from '../artifacts/StakingProxy.json';
|
|||||||
import * as StaticCallProxy from '../artifacts/StaticCallProxy.json';
|
import * as StaticCallProxy from '../artifacts/StaticCallProxy.json';
|
||||||
import * as WETH9 from '../artifacts/WETH9.json';
|
import * as WETH9 from '../artifacts/WETH9.json';
|
||||||
import * as ZRXToken from '../artifacts/ZRXToken.json';
|
import * as ZRXToken from '../artifacts/ZRXToken.json';
|
||||||
|
import * as ERC20BridgeProxy from '../artifacts/ERC20BridgeProxy.json';
|
||||||
|
import * as ZrxVault from '../artifacts/ZrxVault.json';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AssetProxyOwner,
|
AssetProxyOwner,
|
||||||
@ -32,6 +34,7 @@ export {
|
|||||||
DummyERC721Token,
|
DummyERC721Token,
|
||||||
ERC1155Mintable,
|
ERC1155Mintable,
|
||||||
ERC1155Proxy,
|
ERC1155Proxy,
|
||||||
|
ERC20BridgeProxy,
|
||||||
ERC20Proxy,
|
ERC20Proxy,
|
||||||
ERC20Token,
|
ERC20Token,
|
||||||
ERC721Proxy,
|
ERC721Proxy,
|
||||||
@ -50,4 +53,5 @@ export {
|
|||||||
CoordinatorRegistry,
|
CoordinatorRegistry,
|
||||||
Staking,
|
Staking,
|
||||||
StakingProxy,
|
StakingProxy,
|
||||||
|
ZrxVault,
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
"./artifacts/ERC1155Proxy.json",
|
"./artifacts/ERC1155Proxy.json",
|
||||||
"./artifacts/StaticCallProxy.json",
|
"./artifacts/StaticCallProxy.json",
|
||||||
"./artifacts/Staking.json",
|
"./artifacts/Staking.json",
|
||||||
"./artifacts/StakingProxy.json"
|
"./artifacts/StakingProxy.json",
|
||||||
|
"./artifacts/ZrxVault.json",
|
||||||
|
"./artifacts/ERC20BridgeProxy.json"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Migrations script no longer deploys DutchAuction since it is not yet upgraded for V3 of the protocol",
|
"note": "Migrations script no longer deploys DutchAuction since it is not yet upgraded for V3 of the protocol",
|
||||||
"pr": 2324
|
"pr": 2324
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `Staking` and `ERC20BridgeProxy` contracts",
|
||||||
|
"pr": 2323
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { CoordinatorContract, OrderValidatorContract } from '@0x/abi-gen-wrappers';
|
import { CoordinatorContract } from '@0x/abi-gen-wrappers';
|
||||||
import { ContractAddresses } from '@0x/contract-addresses';
|
import { ContractAddresses } from '@0x/contract-addresses';
|
||||||
import * as artifacts from '@0x/contract-artifacts';
|
import * as artifacts from '@0x/contract-artifacts';
|
||||||
import {
|
import {
|
||||||
ERC1155ProxyContract,
|
ERC1155ProxyContract,
|
||||||
|
ERC20BridgeProxyContract,
|
||||||
ERC20ProxyContract,
|
ERC20ProxyContract,
|
||||||
ERC721ProxyContract,
|
ERC721ProxyContract,
|
||||||
MultiAssetProxyContract,
|
MultiAssetProxyContract,
|
||||||
@ -11,15 +12,15 @@ import {
|
|||||||
import { CoordinatorRegistryContract } from '@0x/contracts-coordinator';
|
import { CoordinatorRegistryContract } from '@0x/contracts-coordinator';
|
||||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||||
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||||
import { DummyERC20TokenContract, WETH9Contract, ZRXTokenContract } from '@0x/contracts-erc20';
|
import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
import { ExchangeContract } from '@0x/contracts-exchange';
|
||||||
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
import { ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
||||||
|
import { StakingProxyContract, TestStakingContract, ZrxVaultContract } from '@0x/contracts-staking';
|
||||||
import { Web3ProviderEngine } from '@0x/subproviders';
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
||||||
import { AbiEncoder, BigNumber, providerUtils } from '@0x/utils';
|
import { AbiEncoder, BigNumber, providerUtils } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { MethodAbi, SupportedProvider, TxData } from 'ethereum-types';
|
import { MethodAbi, SupportedProvider, TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { constants } from './utils/constants';
|
import { constants } from './utils/constants';
|
||||||
import { erc20TokenInfo, erc721TokenInfo } from './utils/token_info';
|
import { erc20TokenInfo, erc721TokenInfo } from './utils/token_info';
|
||||||
@ -83,19 +84,21 @@ export async function runMigrationsAsync(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ZRX
|
// ZRX
|
||||||
const zrxToken = await ZRXTokenContract.deployFrom0xArtifactAsync(
|
const zrxToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.ZRXToken,
|
artifacts.DummyERC20Token,
|
||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
'0x Protocol Token',
|
||||||
|
'ZRX',
|
||||||
|
new BigNumber(18),
|
||||||
|
new BigNumber(1000000000000000000000000000),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ether token
|
// Ether token
|
||||||
const etherToken = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults, artifacts);
|
const etherToken = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults, artifacts);
|
||||||
|
|
||||||
// Exchange
|
// Exchange
|
||||||
// tslint:disable-next-line:no-unused-variable
|
|
||||||
const zrxAssetData = encodeERC20AssetData(zrxToken.address);
|
|
||||||
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.Exchange,
|
artifacts.Exchange,
|
||||||
provider,
|
provider,
|
||||||
@ -184,76 +187,13 @@ export async function runMigrationsAsync(
|
|||||||
encodeERC20AssetData(etherToken.address),
|
encodeERC20AssetData(etherToken.address),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO(fabio): Remove orderValidator after @0x/asset-buyer is deleted
|
|
||||||
// OrderValidator
|
|
||||||
const orderValidator = await OrderValidatorContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.OrderValidator,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
exchange.address,
|
|
||||||
zrxAssetData,
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO(fabio): Uncomment dutchAuction once the @0x/contracts-extensions is refactored
|
|
||||||
// for V3
|
|
||||||
// // DutchAuction
|
|
||||||
// const dutchAuction = await DutchAuctionContract.deployFrom0xArtifactAsync(
|
|
||||||
// artifacts.DutchAuction,
|
|
||||||
// provider,
|
|
||||||
// txDefaults,
|
|
||||||
// artifacts,
|
|
||||||
// exchange.address,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// TODO (xianny): figure out how to deploy AssetProxyOwnerContract properly
|
|
||||||
// // Multisigs
|
|
||||||
// const accounts: string[] = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
// const owners = _.uniq([accounts[0], accounts[1], txDefaults.from]);
|
|
||||||
// const confirmationsRequired = new BigNumber(2);
|
|
||||||
// const secondsRequired = new BigNumber(0);
|
|
||||||
|
|
||||||
// // AssetProxyOwner
|
|
||||||
|
|
||||||
// const assetProxyOwner = await AssetProxyOwnerContract.deployFrom0xArtifactAsync(
|
|
||||||
// artifacts.AssetProxyOwner,
|
|
||||||
// provider,
|
|
||||||
// txDefaults,
|
|
||||||
// artifacts,
|
|
||||||
// [],
|
|
||||||
// [erc20Proxy.address, erc721Proxy.address, multiAssetProxy.address],
|
|
||||||
// [],
|
|
||||||
// owners,
|
|
||||||
// confirmationsRequired,
|
|
||||||
// secondsRequired,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Transfer Ownership to the Asset Proxy Owner
|
|
||||||
// await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
// await erc20Proxy.transferOwnership(assetProxyOwner.address).sendTransactionAsync(txDefaults),
|
|
||||||
// );
|
|
||||||
// await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
// await erc721Proxy.transferOwnership(assetProxyOwner.address).sendTransactionAsync(txDefaults),
|
|
||||||
// );
|
|
||||||
// await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
// await erc1155Proxy.transferOwnership(assetProxyOwner.address).sendTransactionAsync(txDefaults),
|
|
||||||
// );
|
|
||||||
// await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
// await multiAssetProxy.transferOwnership(assetProxyOwner.address).sendTransactionAsync(txDefaults),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// Fake the above transactions so our nonce increases and we result with the same addresses
|
// Fake the above transactions so our nonce increases and we result with the same addresses
|
||||||
// while AssetProxyOwner is disabled (TODO: @dekz remove)
|
// while AssetProxyOwner is disabled (TODO: @dekz remove)
|
||||||
const dummyTransactionCount = 6;
|
const dummyTransactionCount = 8;
|
||||||
for (let index = 0; index < dummyTransactionCount; index++) {
|
for (let index = 0; index < dummyTransactionCount; index++) {
|
||||||
await web3Wrapper.sendTransactionAsync({ to: txDefaults.from, from: txDefaults.from, value: new BigNumber(0) });
|
await web3Wrapper.sendTransactionAsync({ to: txDefaults.from, from: txDefaults.from, value: new BigNumber(0) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fund the Forwarder with ZRX
|
|
||||||
const zrxDecimals = await zrxToken.decimals().callAsync();
|
|
||||||
const zrxForwarderAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5000), zrxDecimals);
|
|
||||||
await zrxToken.transfer(forwarder.address, zrxForwarderAmount).awaitTransactionSuccessAsync(txDefaults);
|
|
||||||
|
|
||||||
// CoordinatorRegistry
|
// CoordinatorRegistry
|
||||||
const coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
const coordinatorRegistry = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.CoordinatorRegistry,
|
artifacts.CoordinatorRegistry,
|
||||||
@ -288,6 +228,59 @@ export async function runMigrationsAsync(
|
|||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const erc20BridgeProxy = await ERC20BridgeProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ERC20BridgeProxy,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
await exchange.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await erc20BridgeProxy.addAuthorizedAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await erc20BridgeProxy.addAuthorizedAddress(multiAssetProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await multiAssetProxy.registerAssetProxy(erc20BridgeProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
|
const zrxProxy = erc20Proxy.address;
|
||||||
|
const zrxVault = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ZrxVault,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
zrxProxy,
|
||||||
|
zrxToken.address,
|
||||||
|
);
|
||||||
|
await erc20Proxy.addAuthorizedAddress(zrxVault.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
|
// Note we use TestStakingContract as the deployed bytecode of a StakingContract
|
||||||
|
// has the tokens hardcoded
|
||||||
|
const stakingLogic = await TestStakingContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Staking,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
etherToken.address,
|
||||||
|
zrxVault.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
const stakingProxy = await StakingProxyContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.StakingProxy,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
{},
|
||||||
|
stakingLogic.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reference the Proxy as the StakingContract for setup
|
||||||
|
const stakingDel = await new TestStakingContract(stakingProxy.address, provider, txDefaults);
|
||||||
|
await stakingProxy.addAuthorizedAddress(txDefaults.from).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await stakingDel.addExchangeAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await exchange.setProtocolFeeCollectorAddress(stakingProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await exchange.setProtocolFeeMultiplier(new BigNumber(150000)).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
|
await zrxVault.addAuthorizedAddress(txDefaults.from).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await zrxVault.setStakingProxy(stakingProxy.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await stakingLogic.addAuthorizedAddress(txDefaults.from).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
await stakingLogic.addExchangeAddress(exchange.address).awaitTransactionSuccessAsync(txDefaults);
|
||||||
|
|
||||||
const contractAddresses = {
|
const contractAddresses = {
|
||||||
erc20Proxy: erc20Proxy.address,
|
erc20Proxy: erc20Proxy.address,
|
||||||
erc721Proxy: erc721Proxy.address,
|
erc721Proxy: erc721Proxy.address,
|
||||||
@ -295,12 +288,11 @@ export async function runMigrationsAsync(
|
|||||||
zrxToken: zrxToken.address,
|
zrxToken: zrxToken.address,
|
||||||
etherToken: etherToken.address,
|
etherToken: etherToken.address,
|
||||||
exchange: exchange.address,
|
exchange: exchange.address,
|
||||||
// TODO (xianny): figure out how to deploy AssetProxyOwnerContract
|
|
||||||
assetProxyOwner: constants.NULL_ADDRESS,
|
assetProxyOwner: constants.NULL_ADDRESS,
|
||||||
erc20BridgeProxy: constants.NULL_ADDRESS,
|
erc20BridgeProxy: erc20BridgeProxy.address,
|
||||||
zeroExGovernor: constants.NULL_ADDRESS,
|
zeroExGovernor: constants.NULL_ADDRESS,
|
||||||
forwarder: forwarder.address,
|
forwarder: forwarder.address,
|
||||||
orderValidator: orderValidator.address,
|
orderValidator: constants.NULL_ADDRESS,
|
||||||
dutchAuction: constants.NULL_ADDRESS,
|
dutchAuction: constants.NULL_ADDRESS,
|
||||||
coordinatorRegistry: coordinatorRegistry.address,
|
coordinatorRegistry: coordinatorRegistry.address,
|
||||||
coordinator: coordinator.address,
|
coordinator: coordinator.address,
|
||||||
@ -308,10 +300,9 @@ export async function runMigrationsAsync(
|
|||||||
staticCallProxy: staticCallProxy.address,
|
staticCallProxy: staticCallProxy.address,
|
||||||
devUtils: devUtils.address,
|
devUtils: devUtils.address,
|
||||||
exchangeV2: constants.NULL_ADDRESS,
|
exchangeV2: constants.NULL_ADDRESS,
|
||||||
zrxVault: constants.NULL_ADDRESS,
|
zrxVault: zrxVault.address,
|
||||||
readOnlyProxy: constants.NULL_ADDRESS,
|
staking: stakingLogic.address,
|
||||||
staking: constants.NULL_ADDRESS,
|
stakingProxy: stakingProxy.address,
|
||||||
stakingProxy: constants.NULL_ADDRESS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return contractAddresses;
|
return contractAddresses;
|
||||||
|
@ -25,8 +25,8 @@ export const erc20TokenInfo: ERC20Token[] = [
|
|||||||
swarmHash: NULL_BYTES,
|
swarmHash: NULL_BYTES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Digix DAO Token',
|
name: 'Dai',
|
||||||
symbol: 'DGD',
|
symbol: 'DAI',
|
||||||
decimals: new BigNumber(18),
|
decimals: new BigNumber(18),
|
||||||
ipfsHash: NULL_BYTES,
|
ipfsHash: NULL_BYTES,
|
||||||
swarmHash: NULL_BYTES,
|
swarmHash: NULL_BYTES,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user