Working towards maker signature validation
This commit is contained in:
@@ -54,6 +54,7 @@
|
||||
"@0x/dev-utils": "^2.3.0",
|
||||
"@0x/sol-compiler": "^3.1.12",
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@0x/utils": "^4.3.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "*",
|
||||
|
@@ -24,6 +24,7 @@ import "../immutable/MixinConstants.sol";
|
||||
import "../interfaces/IStakingEvents.sol";
|
||||
import "./MixinRewardVault.sol";
|
||||
import "./MixinSignatureValidator.sol";
|
||||
import "../libs/LibEIP712Hash.sol";
|
||||
|
||||
contract MixinPools is
|
||||
SafeMath,
|
||||
@@ -79,7 +80,7 @@ contract MixinPools is
|
||||
);
|
||||
|
||||
require(
|
||||
_isValidSignature(hashSignedByMaker, makerAddress, makerSignature),
|
||||
_isValidMakerSignature(poolId, makerAddress, makerSignature),
|
||||
"INVALID_MAKER_SIGNATURE"
|
||||
);
|
||||
|
||||
@@ -101,14 +102,32 @@ contract MixinPools is
|
||||
_unrecordMaker(poolId, makerAddress);
|
||||
}
|
||||
|
||||
/*
|
||||
function _isValidMakerSignature(address makerAddress, bytes memory makerSignature)
|
||||
function _isValidMakerSignature(bytes32 poolId, address makerAddress, bytes memory makerSignature)
|
||||
internal
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
|
||||
bytes32 approvalHash = _getStakingPoolApprovalMessageHash(poolId, makerAddress);
|
||||
isValid = _isValidSignature(approvalHash, makerAddress, makerSignature);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function _getStakingPoolApprovalMessageHash(bytes32 poolId, address makerAddress)
|
||||
internal
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
StakingPoolApproval memory approval = StakingPoolApproval({
|
||||
poolId: poolId,
|
||||
makerAddress: makerAddress
|
||||
});
|
||||
|
||||
// Hash approval message and check signer address
|
||||
address verifierAddress = address(this);
|
||||
approvalHash = LibEIP712Hash._hashStakingPoolApprovalMessage(approval, verifierAddress, CHAIN_ID);
|
||||
|
||||
return approvalHash;
|
||||
}
|
||||
*/
|
||||
|
||||
function _getMakerPoolId(address makerAddress)
|
||||
internal
|
||||
|
@@ -16,10 +16,12 @@ pragma solidity ^0.5.5;
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
import "../interfaces/IWallet.sol";
|
||||
import "../immutable/MixinConstants.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidator is
|
||||
IStructs
|
||||
IStructs,
|
||||
MixinConstants
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
@@ -121,17 +123,7 @@ contract MixinSignatureValidator is
|
||||
// Signature verified by wallet contract.
|
||||
// If used with an order, the maker of the order is the wallet contract.
|
||||
} else if (signatureType == SignatureType.Wallet) {
|
||||
isValid = isValidWalletSignature(
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
);
|
||||
return isValid;
|
||||
|
||||
// Signature verified by wallet contract.
|
||||
// If used with an order, the maker of the order is the wallet contract.
|
||||
} else if (signatureType == SignatureType.Wallet) {
|
||||
isValid = isValidWalletSignature(
|
||||
isValid = _isValidWalletSignature(
|
||||
hash,
|
||||
signerAddress,
|
||||
signature
|
||||
@@ -153,7 +145,7 @@ contract MixinSignatureValidator is
|
||||
/// and defines its own signature verification method.
|
||||
/// @param signature Proof that the hash has been signed by signer.
|
||||
/// @return True if signature is valid for given wallet..
|
||||
function isValidWalletSignature(
|
||||
function _isValidWalletSignature(
|
||||
bytes32 hash,
|
||||
address walletAddress,
|
||||
bytes memory signature
|
||||
@@ -162,36 +154,28 @@ contract MixinSignatureValidator is
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
// contruct hash as bytes, so that it is a valid EIP-1271 payload
|
||||
bytes memory hashAsBytes = new bytes(32);
|
||||
assembly {
|
||||
mstore(add(hashAsBytes, 32), hash)
|
||||
}
|
||||
|
||||
// Static call `isValidSignature` in the destination wallet
|
||||
bytes memory callData = abi.encodeWithSelector(
|
||||
IWallet(walletAddress).isValidSignature.selector,
|
||||
hash,
|
||||
signature
|
||||
);
|
||||
assembly {
|
||||
let cdStart := add(callData, 32)
|
||||
let success := staticcall(
|
||||
gas, // forward all gas
|
||||
walletAddress, // address of Wallet contract
|
||||
cdStart, // pointer to start of input
|
||||
mload(callData), // length of input
|
||||
cdStart, // write output over input
|
||||
32 // output size is 32 bytes
|
||||
)
|
||||
(bool success, bytes memory result) = walletAddress.staticcall(callData);
|
||||
|
||||
switch success
|
||||
case 0 {
|
||||
// Revert with `Error("WALLET_ERROR")`
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)
|
||||
mstore(96, 0)
|
||||
revert(0, 100)
|
||||
}
|
||||
case 1 {
|
||||
// Signature is valid if call did not revert and returned true
|
||||
isValid := mload(cdStart)
|
||||
}
|
||||
}
|
||||
// Sanity check call and extract the magic value
|
||||
require(
|
||||
success,
|
||||
"WALLET_ERROR"
|
||||
);
|
||||
bytes4 magicValue = result.readBytes4(0);
|
||||
|
||||
isValid = (magicValue == EIP1271_MAGIC_VALUE);
|
||||
return isValid;
|
||||
}
|
||||
}
|
@@ -35,6 +35,9 @@ contract MixinConstants {
|
||||
|
||||
uint64 constant public INITIAL_TIMELOCK_PERIOD = INITIAL_EPOCH;
|
||||
|
||||
// bytes4(keccak256("isValidSignature(bytes,bytes)")
|
||||
bytes4 constant internal EIP1271_MAGIC_VALUE = 0x20c13b0b;
|
||||
|
||||
uint64 constant public EPOCH_PERIOD_IN_SECONDS = 1000; // @TODO SET FOR DEPLOYMENT
|
||||
|
||||
uint64 constant public TIMELOCK_PERIOD_IN_EPOCHS = 3; // @TODO SET FOR DEPLOYMENT
|
||||
@@ -42,4 +45,6 @@ contract MixinConstants {
|
||||
uint256 constant public COBB_DOUGLAS_ALPHA_DENOMINATOR = 6; // @TODO SET FOR DEPLOYMENT
|
||||
|
||||
uint256 constant public REWARD_PAYOUT_DELEGATED_STAKE_PERCENT_VALUE = 90; // @TODO SET FOR DEPLOYMENT
|
||||
|
||||
uint256 constant public CHAIN_ID = 1; // @TODO SET FOR DEPLOYMENT
|
||||
}
|
||||
|
@@ -31,6 +31,11 @@ interface IStructs {
|
||||
NSignatureTypes // 0x05, number of signature types. Always leave at end.
|
||||
}
|
||||
|
||||
struct StakingPoolApproval {
|
||||
bytes32 poolId;
|
||||
address makerAddress;
|
||||
}
|
||||
|
||||
struct Timelock {
|
||||
uint64 lockedAt;
|
||||
uint96 total;
|
||||
|
@@ -16,15 +16,17 @@ pragma solidity ^0.5.5;
|
||||
|
||||
interface IWallet /* is EIP-1271 */ {
|
||||
|
||||
/// @dev Verifies that a signature is valid.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return The function selector for this function
|
||||
/// @dev Should return whether the signature provided is valid for the provided data
|
||||
/// @param data Arbitrary length data signed on the behalf of address(this)
|
||||
/// @param signature Signature byte array associated with _data
|
||||
///
|
||||
/// MUST return the bytes4 magic value 0x20c13b0b when function passes.
|
||||
/// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
|
||||
/// MUST allow external calls
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
)
|
||||
bytes calldata data,
|
||||
bytes calldata signature)
|
||||
external
|
||||
view
|
||||
returns (bytes32 isValidSignatureSelector);
|
||||
returns (bytes4 magicValue);
|
||||
}
|
100
contracts/staking/contracts/src/libs/LibEIP712Hash.sol
Normal file
100
contracts/staking/contracts/src/libs/LibEIP712Hash.sol
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
||||
import "../interfaces/IStructs.sol";
|
||||
|
||||
|
||||
library LibEIP712Hash
|
||||
{
|
||||
// EIP712 Domain Name value for the Staking contract
|
||||
string constant internal EIP712_STAKING_DOMAIN_NAME = "0x Protocol Staking";
|
||||
|
||||
// EIP712 Domain Version value for the Staking contract
|
||||
string constant internal EIP712_STAKING_DOMAIN_VERSION = "1.0.0";
|
||||
|
||||
// Hash for the EIP712 StakingPool approval message
|
||||
// keccak256(abi.encodePacked(
|
||||
// "StakingPoolApproval(",
|
||||
// "bytes32 poolId,",
|
||||
// "address makerAddress",
|
||||
// ")"
|
||||
// ));
|
||||
bytes32 constant internal EIP712_STAKING_POOL_APPROVAL_SCHEMA_HASH = 0x9b699f12ef1c0f7b43076182dcccc0c548c9a784cfcf27114f98d684e06826b6;
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the StakingPool approval mesasage using the domain separator of this contract.
|
||||
/// @param approval StakingPool approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the StakingPool approval message with the domain separator of this contract.
|
||||
function _hashStakingPoolApprovalMessage(
|
||||
IStructs.StakingPoolApproval memory approval,
|
||||
address verifierAddress,
|
||||
uint256 chainId
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
approvalHash = _hashEIP712StakingMessage(
|
||||
_hashStakingPoolApproval(approval),
|
||||
verifierAddress,
|
||||
chainId
|
||||
);
|
||||
return approvalHash;
|
||||
}
|
||||
|
||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
||||
/// of this contract.
|
||||
/// @param hashStruct The EIP712 hash struct.
|
||||
/// @return EIP712 hash applied to this EIP712 Domain.
|
||||
function _hashEIP712StakingMessage(
|
||||
bytes32 hashStruct,
|
||||
address verifierAddress,
|
||||
uint256 chainId
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
bytes32 eip712StakingDomainHash = LibEIP712._hashEIP712Domain(
|
||||
EIP712_STAKING_DOMAIN_NAME,
|
||||
EIP712_STAKING_DOMAIN_VERSION,
|
||||
chainId,
|
||||
verifierAddress
|
||||
);
|
||||
return LibEIP712._hashEIP712Message(eip712StakingDomainHash, hashStruct);
|
||||
}
|
||||
|
||||
/// @dev Calculated the EIP712 hash of the StakingPool approval mesasage with no domain separator.
|
||||
/// @param approval StakingPool approval message containing the transaction hash, transaction signature, and expiration of the approval.
|
||||
/// @return EIP712 hash of the StakingPool approval message with no domain separator.
|
||||
function _hashStakingPoolApproval(IStructs.StakingPoolApproval memory approval)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 result)
|
||||
{
|
||||
result = keccak256(abi.encodePacked(
|
||||
EIP712_STAKING_POOL_APPROVAL_SCHEMA_HASH,
|
||||
approval.poolId,
|
||||
approval.makerAddress
|
||||
));
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -89,4 +89,20 @@ contract MixinPoolsWrapper is
|
||||
makerAddresses = _getMakerAddressesForPool(makerId);
|
||||
return makerAddresses;
|
||||
}
|
||||
|
||||
function isValidMakerSignature(bytes32 poolId, address makerAddress, bytes calldata makerSignature)
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
return _isValidMakerSignature(poolId, makerAddress, makerSignature);
|
||||
}
|
||||
|
||||
function getStakingPoolApprovalMessageHash(bytes32 poolId, address makerAddress)
|
||||
external
|
||||
view
|
||||
returns (bytes32 approvalHash)
|
||||
{
|
||||
return _getStakingPoolApprovalMessageHash(poolId, makerAddress);
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@
|
||||
pragma solidity ^0.5.5;
|
||||
|
||||
import "../core/MixinSignatureValidator.sol";
|
||||
import "../libs/LibEIP712Hash.sol";
|
||||
|
||||
|
||||
contract MixinSignatureValidatorWrapper is
|
||||
@@ -28,9 +29,9 @@ contract MixinSignatureValidatorWrapper is
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
bytes memory signature
|
||||
bytes calldata signature
|
||||
)
|
||||
internal
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
|
40
contracts/staking/contracts/test/TestSignatureValidator.sol
Normal file
40
contracts/staking/contracts/test/TestSignatureValidator.sol
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||
import "../src/MixinSignatureValidator.sol";
|
||||
import "../src/MixinTransactions.sol";
|
||||
import "../src/MixinExchangeRichErrors.sol";
|
||||
|
||||
|
||||
contract TestSignatureValidator is
|
||||
LibEIP712ExchangeDomain,
|
||||
MixinSignatureValidator,
|
||||
MixinTransactions,
|
||||
MixinExchangeRichErrors
|
||||
{
|
||||
|
||||
// solhint-disable no-empty-blocks
|
||||
constructor (uint256 chainId)
|
||||
public
|
||||
LibEIP712ExchangeDomain(chainId, address(0))
|
||||
{}
|
||||
}
|
@@ -57,6 +57,7 @@
|
||||
"@0x/tslint-config": "^3.0.1",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/node": "*",
|
||||
"@0x/utils": "^4.3.1",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
@@ -71,12 +72,14 @@
|
||||
"typescript": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^5.0.5",
|
||||
"@0x/contracts-utils": "^3.1.1",
|
||||
"@0x/base-contract": "^5.1.0",
|
||||
"@0x/contracts-test-utils": "^3.1.6",
|
||||
"@0x/contracts-utils": "3.1.1",
|
||||
"@0x/order-utils": "^7.2.0",
|
||||
"@0x/types": "^2.2.2",
|
||||
"@0x/typescript-typings": "^4.2.2",
|
||||
"@0x/utils": "^4.3.1",
|
||||
"@0x/web3-wrapper": "^6.0.5",
|
||||
"@0x/web3-wrapper": "^6.0.6",
|
||||
"ethereum-types": "^2.1.2",
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"lodash": "^4.17.11"
|
||||
|
@@ -29,6 +29,7 @@ describe('Staking Core', () => {
|
||||
// constants
|
||||
const ZRX_TOKEN_DECIMALS = new BigNumber(18);
|
||||
// tokens & addresses
|
||||
let accounts: string[];
|
||||
let owner: string;
|
||||
let exchange: string;
|
||||
let stakers: string[];
|
||||
@@ -48,7 +49,7 @@ describe('Staking Core', () => {
|
||||
});
|
||||
before(async () => {
|
||||
// create accounts
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
owner = accounts[0];
|
||||
exchange = accounts[1];
|
||||
stakers = accounts.slice(2, 5);
|
||||
@@ -774,7 +775,7 @@ describe('Staking Core', () => {
|
||||
expect(ownerRewardFloatingPoint).to.be.bignumber.equal(expectedOwnerReward);
|
||||
});
|
||||
|
||||
it('pool management', async() => {
|
||||
it.only('pool management', async() => {
|
||||
// create first pool
|
||||
const operatorAddress = stakers[0];
|
||||
const operatorShare = 39;
|
||||
@@ -786,8 +787,15 @@ describe('Staking Core', () => {
|
||||
expect(nextPoolId).to.be.equal(expectedNextPoolId);
|
||||
// add maker to pool
|
||||
const makerAddress = makers[0];
|
||||
const makerSignature = "0x";
|
||||
await stakingWrapper.addMakerToPoolAsync(poolId, makerAddress, "0x00", makerSignature, operatorAddress);
|
||||
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||
const makerApproval = stakingWrapper.getSignedApprovalForStakingPool(poolId, makerAddress, makerPrivateKey);
|
||||
|
||||
const onchainHash = await stakingWrapper.getStakingPoolApprovalMessageHashAsync(poolId, makerAddress);
|
||||
console.log(`** ON-CHAIN HASH **\n`, onchainHash);
|
||||
|
||||
console.log(JSON.stringify(makerApproval, null , 4));
|
||||
await stakingWrapper.addMakerToPoolAsync(poolId, makerAddress, "0x00", makerApproval.signature, operatorAddress);
|
||||
throw new Error(`ADDED SUCCESSFULLY!`);
|
||||
// check the pool id of the maker
|
||||
const poolIdOfMaker = await stakingWrapper.getMakerPoolId(makerAddress);
|
||||
expect(poolIdOfMaker).to.be.equal(poolId);
|
||||
@@ -796,15 +804,16 @@ describe('Staking Core', () => {
|
||||
expect(makerAddressesForPool).to.be.deep.equal([makerAddress]);
|
||||
// try to add the same maker address again
|
||||
await expectTransactionFailedAsync(
|
||||
stakingWrapper.addMakerToPoolAsync(poolId, makerAddress, "0x00", makerSignature, operatorAddress),
|
||||
stakingWrapper.addMakerToPoolAsync(poolId, makerAddress, "0x00", makerApproval.signature, operatorAddress),
|
||||
RevertReason.MakerAddressAlreadyRegistered
|
||||
);
|
||||
// try to add a new maker address from an address other than the pool operator
|
||||
const notOperatorAddress = owner;
|
||||
const anotherMakerAddress = makers[1];
|
||||
const anotherMakerSignature = "0x";
|
||||
const anotherMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(anotherMakerAddress)];
|
||||
const anotherMakerApproval = stakingWrapper.getSignedApprovalForStakingPool(poolId, anotherMakerAddress, anotherMakerPrivateKey);
|
||||
await expectTransactionFailedAsync(
|
||||
stakingWrapper.addMakerToPoolAsync(poolId, anotherMakerAddress, "0x00", anotherMakerSignature, notOperatorAddress),
|
||||
stakingWrapper.addMakerToPoolAsync(poolId, anotherMakerAddress, "0x00", anotherMakerApproval.signature, notOperatorAddress),
|
||||
RevertReason.OnlyCallableByPoolOperator
|
||||
);
|
||||
// try to remove the maker address from an address other than the operator
|
||||
|
42
contracts/staking/test/utils/ApprovalFactory.ts
Normal file
42
contracts/staking/test/utils/ApprovalFactory.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { signingUtils } from '@0x/contracts-test-utils';
|
||||
import { SignatureType } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { hashUtils } from './hash_utils';
|
||||
import { SignedStakingPoolApproval } from './types';
|
||||
|
||||
export class ApprovalFactory {
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _verifyingContractAddress: string;
|
||||
private readonly _chainId: number;
|
||||
|
||||
constructor(privateKey: Buffer, verifyingContractAddress: string, chainId: number) {
|
||||
this._privateKey = privateKey;
|
||||
this._verifyingContractAddress = verifyingContractAddress;
|
||||
this._chainId = chainId;
|
||||
}
|
||||
|
||||
public newSignedApproval(
|
||||
poolId: string,
|
||||
makerAddress: string,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedStakingPoolApproval {
|
||||
const approvalHashBuff = hashUtils.getStakingPoolApprovalHashBuffer(
|
||||
poolId,
|
||||
makerAddress,
|
||||
this._verifyingContractAddress,
|
||||
this._chainId
|
||||
);
|
||||
console.log('*** APPROVAL HASH ***\n', approvalHashBuff);
|
||||
const signatureBuff = signingUtils.signMessage(approvalHashBuff, this._privateKey, signatureType);
|
||||
const signedApproval = {
|
||||
makerAddress,
|
||||
poolId,
|
||||
verifyingContractAddress: this._verifyingContractAddress,
|
||||
chainId: this._chainId,
|
||||
signature: ethUtil.addHexPrefix(signatureBuff.toString('hex')),
|
||||
};
|
||||
return signedApproval;
|
||||
}
|
||||
}
|
@@ -10,4 +10,5 @@ export const constants = {
|
||||
INITIAL_TIMELOCK_PERIOD: new BigNumber(0),
|
||||
EPOCH_PERIOD_IN_SECONDS: new BigNumber(1000), // @TODO SET FOR DEPLOYMENT*/
|
||||
TIMELOCK_PERIOD_IN_EPOCHS: new BigNumber(3), // @TODO SET FOR DEPLOYMENT
|
||||
CHAIN_ID: 1,
|
||||
};
|
32
contracts/staking/test/utils/hash_utils.ts
Normal file
32
contracts/staking/test/utils/hash_utils.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const hashUtils = {
|
||||
getStakingPoolApprovalHashBuffer(
|
||||
poolId: string,
|
||||
makerAddress: string,
|
||||
verifyingContractAddress: string,
|
||||
chainId: number
|
||||
): Buffer {
|
||||
const typedData = eip712Utils.createStakingPoolApprovalTypedData(
|
||||
poolId,
|
||||
makerAddress,
|
||||
verifyingContractAddress,
|
||||
chainId
|
||||
);
|
||||
const hashBuffer = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return hashBuffer;
|
||||
},
|
||||
getStakingPoolApprovalHashHex(
|
||||
poolId: string,
|
||||
makerAddress: string,
|
||||
verifyingContractAddress: string,
|
||||
chainId: number
|
||||
): string {
|
||||
const hashHex = `0x${hashUtils
|
||||
.getStakingPoolApprovalHashBuffer(poolId, makerAddress, verifyingContractAddress, chainId)
|
||||
.toString('hex')}`;
|
||||
return hashHex;
|
||||
},
|
||||
};
|
@@ -1,14 +1,18 @@
|
||||
import { constants, LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { SignatureType } from '@0x/types';
|
||||
import { LogWithDecodedArgs, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { ERC20ProxyContract } from '@0x/contracts-asset-proxy';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, StakingEEventArgs, StakingContract, StakingProxyContract, ZrxVaultContract, RewardVaultContract, LibMathTestContract } from '../../src';
|
||||
import { ApprovalFactory } from './ApprovalFactory';
|
||||
import { SignedStakingPoolApproval } from './types';
|
||||
import { constants } from './constants';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
@@ -272,6 +276,41 @@ export class StakingWrapper {
|
||||
const makerAddresses = this.getStakingContract().getMakerAddressesForPool.getABIDecodedReturnData(returndata);
|
||||
return makerAddresses;
|
||||
}
|
||||
public async isValidMakerSignatureAsync(poolId: string, makerAddress: string, makerSignature: string): Promise<Boolean> {
|
||||
const calldata = this.getStakingContract().isValidMakerSignature.getABIEncodedTransactionData(poolId, makerAddress, makerSignature);
|
||||
const returndata = await this._callAsync(calldata);
|
||||
const isValid = this.getStakingContract().isValidMakerSignature.getABIDecodedReturnData(returndata);
|
||||
return isValid;
|
||||
}
|
||||
public async getStakingPoolApprovalMessageHashAsync(poolId: string, makerAddress: string): Promise<string> {
|
||||
const calldata = this.getStakingContract().getStakingPoolApprovalMessageHash.getABIEncodedTransactionData(poolId, makerAddress);
|
||||
const returndata = await this._callAsync(calldata);
|
||||
const messageHash = this.getStakingContract().getStakingPoolApprovalMessageHash.getABIDecodedReturnData(returndata);
|
||||
return messageHash;
|
||||
}
|
||||
public getSignedApprovalForStakingPool(poolId: string, makerAddress: string, makerPrivateKey: Buffer, signatureType: SignatureType = SignatureType.EthSign): SignedStakingPoolApproval {
|
||||
const signedStakingPoolApproval = this.getSignedApprovalForStakingPoolFlexible(
|
||||
poolId,
|
||||
makerAddress,
|
||||
makerPrivateKey,
|
||||
this.getStakingProxyContract().address,
|
||||
constants.CHAIN_ID,
|
||||
signatureType
|
||||
);
|
||||
return signedStakingPoolApproval;
|
||||
}
|
||||
public getSignedApprovalForStakingPoolFlexible(
|
||||
poolId: string,
|
||||
makerAddress: string,
|
||||
makerPrivateKey: Buffer,
|
||||
verifierAddress: string,
|
||||
chainId: number,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): SignedStakingPoolApproval {
|
||||
const approvalFactory = new ApprovalFactory(makerPrivateKey, verifierAddress, chainId);
|
||||
const signedStakingPoolApproval = approvalFactory.newSignedApproval(poolId, makerAddress, signatureType);
|
||||
return signedStakingPoolApproval;
|
||||
}
|
||||
///// EPOCHS /////
|
||||
public async goToNextEpochAsync(): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const calldata = this.getStakingContract().finalizeFees.getABIEncodedTransactionData();
|
||||
|
12
contracts/staking/test/utils/types.ts
Normal file
12
contracts/staking/test/utils/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
export interface StakingPoolApproval {
|
||||
makerAddress: string,
|
||||
poolId: string,
|
||||
verifyingContractAddress: string,
|
||||
chainId: number
|
||||
}
|
||||
|
||||
export interface SignedStakingPoolApproval extends StakingPoolApproval {
|
||||
signature: string;
|
||||
}
|
Reference in New Issue
Block a user