Merge pull request #349 from 0xProject/fix/0x.js/signOrderHash
Add shouldAddPersonalMessagePrefix param to signOrderHashAsync
This commit is contained in:
commit
7603cef308
@ -4,8 +4,8 @@
|
|||||||
"workspaces": ["packages/*"],
|
"workspaces": ["packages/*"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
|
"testrpc": "testrpc -p 8545 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
|
||||||
"prettier": "prettier --write '**/*.{ts,tsx,json,md}'",
|
"prettier": "prettier --write '**/*.{ts,tsx,json,md}' --config .prettierrc",
|
||||||
"prettier:ci": "prettier --list-different '**/*.{ts,tsx,json,md}'",
|
"prettier:ci": "prettier --list-different '**/*.{ts,tsx,json,md}' --config .prettierrc",
|
||||||
"lerna:run": "lerna run",
|
"lerna:run": "lerna run",
|
||||||
"lerna:rebuild": "lerna run clean; lerna run build;",
|
"lerna:rebuild": "lerna run clean; lerna run build;",
|
||||||
"lerna:publish":
|
"lerna:publish":
|
||||||
|
@ -1,10 +1,21 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## v0.30.1 - _TBD, 2018_
|
## v0.31.0 - _January 30, 2018_
|
||||||
|
|
||||||
|
* Add the `shouldAddPersonalMessagePrefix` parameter to `signOrderHashAsync` so that the
|
||||||
|
caller can decide on whether to add the personalMessage prefix before relaying the request
|
||||||
|
to the signer. Parity Signer, Ledger and TestRPC add the prefix themselves, Metamask expects
|
||||||
|
it to have already been added. (#349)
|
||||||
|
|
||||||
|
## v0.30.2 - _January 29, 2018_
|
||||||
|
|
||||||
|
* Add Rinkeby testnet addresses to artifacts (#337)
|
||||||
|
* Move @0xproject/types to dependencies from devDependencies fixing missing type errors
|
||||||
|
|
||||||
|
## v0.30.1 - _January 24, 2018_
|
||||||
|
|
||||||
* Fix a bug allowing negative fill values (#212)
|
* Fix a bug allowing negative fill values (#212)
|
||||||
* Fix a bug that made it impossible to pass a custom ZRX address (#341)
|
* Fix a bug that made it impossible to pass a custom ZRX address (#341)
|
||||||
* Add Rinkeby testnet addresses to artifacts (#337)
|
|
||||||
|
|
||||||
## v0.30.0 - _January 17, 2018_
|
## v0.30.0 - _January 17, 2018_
|
||||||
|
|
||||||
|
@ -2,27 +2,24 @@
|
|||||||
"name": "0x.js",
|
"name": "0x.js",
|
||||||
"version": "0.30.2",
|
"version": "0.30.2",
|
||||||
"description": "A javascript library for interacting with the 0x protocol",
|
"description": "A javascript library for interacting with the 0x protocol",
|
||||||
"keywords": [
|
"keywords": ["0x.js", "0xproject", "ethereum", "tokens", "exchange"],
|
||||||
"0x.js",
|
|
||||||
"0xproject",
|
|
||||||
"ethereum",
|
|
||||||
"tokens",
|
|
||||||
"exchange"
|
|
||||||
],
|
|
||||||
"main": "lib/src/index.js",
|
"main": "lib/src/index.js",
|
||||||
"types": "lib/src/index.d.ts",
|
"types": "lib/src/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "run-s clean generate_contract_wrappers",
|
"prebuild": "run-s clean generate_contract_wrappers",
|
||||||
"build": "run-p build:umd:prod build:commonjs; exit 0;",
|
"build": "run-p build:umd:prod build:commonjs; exit 0;",
|
||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
|
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
|
||||||
"upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
|
"upload_docs_json":
|
||||||
"generate_contract_wrappers": "node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
|
"aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
|
||||||
|
"generate_contract_wrappers":
|
||||||
|
"node ../abi-gen/lib/index.js --abiGlob 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --templates contract_templates --output src/contract_wrappers/generated",
|
||||||
"lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
|
"lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
|
||||||
"test:circleci": "run-s test:coverage report_test_coverage",
|
"test:circleci": "run-s test:coverage report_test_coverage",
|
||||||
"test": "run-s clean test:commonjs",
|
"test": "run-s clean test:commonjs",
|
||||||
"test:coverage": "nyc npm run test --all",
|
"test:coverage": "nyc npm run test --all",
|
||||||
"report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
|
"report_test_coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||||
"update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
|
"update_contracts":
|
||||||
|
"for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../0x.js/src/artifacts; done;",
|
||||||
"clean": "shx rm -rf _bundles lib test_temp",
|
"clean": "shx rm -rf _bundles lib test_temp",
|
||||||
"build:umd:prod": "NODE_ENV=production webpack",
|
"build:umd:prod": "NODE_ENV=production webpack",
|
||||||
"build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
|
"build:commonjs": "tsc && copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts;",
|
||||||
|
@ -240,20 +240,22 @@ export class ZeroEx {
|
|||||||
* @param orderHash Hex encoded orderHash to sign.
|
* @param orderHash Hex encoded orderHash to sign.
|
||||||
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
|
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
|
||||||
* must be available via the Web3.Provider supplied to 0x.js.
|
* must be available via the Web3.Provider supplied to 0x.js.
|
||||||
|
* @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message`
|
||||||
|
* themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client
|
||||||
|
* (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix
|
||||||
|
* before sending the request.
|
||||||
* @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
|
* @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
|
||||||
*/
|
*/
|
||||||
public async signOrderHashAsync(orderHash: string, signerAddress: string): Promise<ECSignature> {
|
public async signOrderHashAsync(
|
||||||
|
orderHash: string,
|
||||||
|
signerAddress: string,
|
||||||
|
shouldAddPersonalMessagePrefix: boolean,
|
||||||
|
): Promise<ECSignature> {
|
||||||
assert.isHexString('orderHash', orderHash);
|
assert.isHexString('orderHash', orderHash);
|
||||||
await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
|
await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
|
||||||
|
|
||||||
let msgHashHex;
|
let msgHashHex = orderHash;
|
||||||
const nodeVersion = await this._web3Wrapper.getNodeVersionAsync();
|
if (shouldAddPersonalMessagePrefix) {
|
||||||
const isParityNode = utils.isParityNode(nodeVersion);
|
|
||||||
const isTestRpc = utils.isTestRpc(nodeVersion);
|
|
||||||
if (isParityNode || isTestRpc) {
|
|
||||||
// Parity and TestRpc nodes add the personalMessage prefix itself
|
|
||||||
msgHashHex = orderHash;
|
|
||||||
} else {
|
|
||||||
const orderHashBuff = ethUtil.toBuffer(orderHash);
|
const orderHashBuff = ethUtil.toBuffer(orderHash);
|
||||||
const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
|
const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
|
||||||
msgHashHex = ethUtil.bufferToHex(msgHashBuff);
|
msgHashHex = ethUtil.bufferToHex(msgHashBuff);
|
||||||
|
@ -20,12 +20,6 @@ export const utils = {
|
|||||||
// tslint:disable-next-line: no-console
|
// tslint:disable-next-line: no-console
|
||||||
console.log(message);
|
console.log(message);
|
||||||
},
|
},
|
||||||
isParityNode(nodeVersion: string): boolean {
|
|
||||||
return _.includes(nodeVersion, 'Parity');
|
|
||||||
},
|
|
||||||
isTestRpc(nodeVersion: string): boolean {
|
|
||||||
return _.includes(nodeVersion, 'TestRPC');
|
|
||||||
},
|
|
||||||
spawnSwitchErr(name: string, value: any): Error {
|
spawnSwitchErr(name: string, value: any): Error {
|
||||||
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
|
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
|
||||||
},
|
},
|
||||||
|
@ -16,6 +16,8 @@ const blockchainLifecycle = new BlockchainLifecycle(constants.RPC_URL);
|
|||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
|
||||||
|
|
||||||
describe('ZeroEx library', () => {
|
describe('ZeroEx library', () => {
|
||||||
const web3 = web3Factory.create();
|
const web3 = web3Factory.create();
|
||||||
const config = {
|
const config = {
|
||||||
@ -198,7 +200,11 @@ describe('ZeroEx library', () => {
|
|||||||
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
|
||||||
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
|
||||||
};
|
};
|
||||||
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, makerAddress);
|
const ecSignature = await zeroEx.signOrderHashAsync(
|
||||||
|
orderHash,
|
||||||
|
makerAddress,
|
||||||
|
SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
|
||||||
|
);
|
||||||
expect(ecSignature).to.deep.equal(expectedECSignature);
|
expect(ecSignature).to.deep.equal(expectedECSignature);
|
||||||
});
|
});
|
||||||
it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
|
it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
|
||||||
@ -215,7 +221,11 @@ describe('ZeroEx library', () => {
|
|||||||
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
|
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
|
||||||
];
|
];
|
||||||
|
|
||||||
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, makerAddress);
|
const ecSignature = await zeroEx.signOrderHashAsync(
|
||||||
|
orderHash,
|
||||||
|
makerAddress,
|
||||||
|
SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
|
||||||
|
);
|
||||||
expect(ecSignature).to.deep.equal(expectedECSignature);
|
expect(ecSignature).to.deep.equal(expectedECSignature);
|
||||||
});
|
});
|
||||||
it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
|
it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
|
||||||
@ -232,7 +242,11 @@ describe('ZeroEx library', () => {
|
|||||||
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
|
Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
|
||||||
];
|
];
|
||||||
|
|
||||||
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, makerAddress);
|
const ecSignature = await zeroEx.signOrderHashAsync(
|
||||||
|
orderHash,
|
||||||
|
makerAddress,
|
||||||
|
SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
|
||||||
|
);
|
||||||
expect(ecSignature).to.deep.equal(expectedECSignature);
|
expect(ecSignature).to.deep.equal(expectedECSignature);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,8 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { SignedOrder, ZeroEx } from '../../src';
|
import { SignedOrder, ZeroEx } from '../../src';
|
||||||
|
|
||||||
|
const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
|
||||||
|
|
||||||
export const orderFactory = {
|
export const orderFactory = {
|
||||||
async createSignedOrderAsync(
|
async createSignedOrderAsync(
|
||||||
zeroEx: ZeroEx,
|
zeroEx: ZeroEx,
|
||||||
@ -37,7 +39,7 @@ export const orderFactory = {
|
|||||||
expirationUnixTimestampSec,
|
expirationUnixTimestampSec,
|
||||||
};
|
};
|
||||||
const orderHash = ZeroEx.getOrderHashHex(order);
|
const orderHash = ZeroEx.getOrderHashHex(order);
|
||||||
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker);
|
const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
|
||||||
const signedOrder: SignedOrder = _.assign(order, { ecSignature });
|
const signedOrder: SignedOrder = _.assign(order, { ecSignature });
|
||||||
return signedOrder;
|
return signedOrder;
|
||||||
},
|
},
|
||||||
|
@ -9,9 +9,7 @@
|
|||||||
"url": "git+https://github.com/0xProject/0x.js.git"
|
"url": "git+https://github.com/0xProject/0x.js.git"
|
||||||
},
|
},
|
||||||
"author": "Fabio Berger",
|
"author": "Fabio Berger",
|
||||||
"contributors": [
|
"contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
|
||||||
"Leonid Logvinov <logvinov.leon@gmail.com>"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/0xProject/0x.js/issues"
|
"url": "https://github.com/0xProject/0x.js/issues"
|
||||||
|
@ -2,20 +2,15 @@
|
|||||||
"name": "@0xproject/connect",
|
"name": "@0xproject/connect",
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"description": "A javascript library for interacting with the standard relayer api",
|
"description": "A javascript library for interacting with the standard relayer api",
|
||||||
"keywords": [
|
"keywords": ["connect", "0xproject", "ethereum", "tokens", "exchange"],
|
||||||
"connect",
|
|
||||||
"0xproject",
|
|
||||||
"ethereum",
|
|
||||||
"tokens",
|
|
||||||
"exchange"
|
|
||||||
],
|
|
||||||
"main": "lib/src/index.js",
|
"main": "lib/src/index.js",
|
||||||
"types": "lib/src/index.d.ts",
|
"types": "lib/src/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"clean": "shx rm -rf _bundles lib test_temp",
|
"clean": "shx rm -rf _bundles lib test_temp",
|
||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
|
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR",
|
||||||
"upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
|
"upload_docs_json":
|
||||||
|
"aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json",
|
||||||
"copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
|
"copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures",
|
||||||
"lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
|
"lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'",
|
||||||
"run_mocha": "mocha lib/test/**/*_test.js",
|
"run_mocha": "mocha lib/test/**/*_test.js",
|
||||||
|
@ -8,9 +8,11 @@
|
|||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf ./lib; copyfiles ./build/**/* ./deploy/solc/solc_bin/* ./deploy/test/fixtures/contracts/**/* ./deploy/test/fixtures/contracts/* ./lib; tsc;",
|
"build":
|
||||||
|
"rm -rf ./lib; copyfiles ./build/**/* ./deploy/solc/solc_bin/* ./deploy/test/fixtures/contracts/**/* ./deploy/test/fixtures/contracts/* ./lib; tsc;",
|
||||||
"test": "npm run build; truffle test",
|
"test": "npm run build; truffle test",
|
||||||
"compile:comment": "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846",
|
"compile:comment":
|
||||||
|
"Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846",
|
||||||
"compile": "node ../deployer/lib/src/cli.js compile",
|
"compile": "node ../deployer/lib/src/cli.js compile",
|
||||||
"clean": "rm -rf ./lib",
|
"clean": "rm -rf ./lib",
|
||||||
"migrate": "node ../deployer/lib/src/cli.js migrate",
|
"migrate": "node ../deployer/lib/src/cli.js migrate",
|
||||||
|
@ -8,22 +8,12 @@
|
|||||||
"clean": "shx rm -rf lib",
|
"clean": "shx rm -rf lib",
|
||||||
"lint": "tslint --project . 'rules/**/*.ts'"
|
"lint": "tslint --project . 'rules/**/*.ts'"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": ["tslint.js", "README.md", "LICENSE"],
|
||||||
"tslint.js",
|
|
||||||
"README.md",
|
|
||||||
"LICENSE"
|
|
||||||
],
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/0xProject/0x.js.git"
|
"url": "git://github.com/0xProject/0x.js.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": ["tslint", "config", "0xProject", "typescript", "ts"],
|
||||||
"tslint",
|
|
||||||
"config",
|
|
||||||
"0xProject",
|
|
||||||
"typescript",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Fabio Berger",
|
"name": "Fabio Berger",
|
||||||
"email": "fabio@0xproject.com"
|
"email": "fabio@0xproject.com"
|
||||||
|
@ -12,9 +12,7 @@
|
|||||||
"url": "git+https://github.com/0xProject/0x.js.git"
|
"url": "git+https://github.com/0xProject/0x.js.git"
|
||||||
},
|
},
|
||||||
"author": "Fabio Berger",
|
"author": "Fabio Berger",
|
||||||
"contributors": [
|
"contributors": ["Leonid Logvinov <logvinov.leon@gmail.com>"],
|
||||||
"Leonid Logvinov <logvinov.leon@gmail.com>"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/0xProject/0x.js/issues"
|
"url": "https://github.com/0xProject/0x.js/issues"
|
||||||
|
@ -8,9 +8,12 @@
|
|||||||
"clean": "shx rm -f public/bundle*",
|
"clean": "shx rm -f public/bundle*",
|
||||||
"lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
|
"lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'",
|
||||||
"dev": "webpack-dev-server --content-base public --https",
|
"dev": "webpack-dev-server --content-base public --https",
|
||||||
"update_contracts": "for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;",
|
"update_contracts":
|
||||||
"deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
|
"for i in ${npm_package_config_artifacts}; do copyfiles -u 4 ../contracts/build/contracts/$i.json ../website/contracts; done;",
|
||||||
"deploy_live": "npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers"
|
"deploy_staging":
|
||||||
|
"npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers",
|
||||||
|
"deploy_live":
|
||||||
|
"npm run build; aws s3 sync ./public/. s3://0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"artifacts": "Mintable"
|
"artifacts": "Mintable"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user