Skip to content
This repository was archived by the owner on Jul 9, 2021. It is now read-only.

Commit dcce827

Browse files
authored
Add decoders for broker and stop-limit data (#2484)
* Add decoders for broker and stop-limit data * update changelogs * Address comments
1 parent fd47947 commit dcce827

File tree

8 files changed

+244
-132
lines changed

8 files changed

+244
-132
lines changed

contracts/broker/CHANGELOG.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
[
2+
{
3+
"version": "1.1.0",
4+
"changes": [
5+
{
6+
"note": "Added decoders for broker data",
7+
"pr": 2484
8+
}
9+
]
10+
},
211
{
312
"timestamp": 1581204851,
413
"version": "1.0.2",
Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,59 @@
11
import { assetDataUtils } from '@0x/order-utils';
2+
import { ERC1155AssetData } from '@0x/types';
23
import { AbiEncoder, BigNumber } from '@0x/utils';
34

4-
export const godsUnchainedUtils = {
5-
/**
6-
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
7-
*/
8-
encodePropertyData(proto: BigNumber, quality: BigNumber): string {
9-
return AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]).encode({
10-
proto,
11-
quality,
12-
});
13-
},
14-
/**
15-
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
16-
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
17-
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
18-
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
19-
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
20-
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
21-
*/
22-
encodeBrokerAssetData(
23-
brokerAddress: string,
24-
godsUnchainedAddress: string,
25-
validatorAddress: string,
26-
proto: BigNumber,
27-
quality: BigNumber,
28-
bundleSize: number = 1,
29-
): string {
30-
const dataEncoder = AbiEncoder.create([
31-
{ name: 'godsUnchainedAddress', type: 'address' },
32-
{ name: 'validatorAddress', type: 'address' },
33-
{ name: 'propertyData', type: 'bytes' },
34-
]);
35-
const propertyData = AbiEncoder.create([
36-
{ name: 'proto', type: 'uint16' },
37-
{ name: 'quality', type: 'uint8' },
38-
]).encode({ proto, quality });
39-
const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
40-
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data);
41-
},
42-
};
5+
export interface GodsUnchainedProperties {
6+
proto: BigNumber | number;
7+
quality: BigNumber | number;
8+
}
9+
10+
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]);
11+
const brokerDataEncoder = AbiEncoder.create([
12+
{ name: 'godsUnchainedAddress', type: 'address' },
13+
{ name: 'validatorAddress', type: 'address' },
14+
{ name: 'propertyData', type: 'bytes' },
15+
]);
16+
17+
/**
18+
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator.
19+
*/
20+
export function encodePropertyData(properties: GodsUnchainedProperties): string {
21+
return propertyDataEncoder.encode(properties);
22+
}
23+
24+
/**
25+
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
26+
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
27+
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
28+
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
29+
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
30+
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
31+
*/
32+
export function encodeBrokerAssetData(
33+
brokerAddress: string,
34+
godsUnchainedAddress: string,
35+
validatorAddress: string,
36+
properties: GodsUnchainedProperties,
37+
bundleSize: number = 1,
38+
): string {
39+
const propertyData = propertyDataEncoder.encode(properties);
40+
const brokerData = brokerDataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
41+
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], brokerData);
42+
}
43+
44+
/**
45+
* Decodes proto and quality from the bytes format expected by the GodsUnchainedValidator.
46+
*/
47+
export function decodePropertyData(propertyData: string): GodsUnchainedProperties {
48+
return propertyDataEncoder.decode(propertyData);
49+
}
50+
51+
/**
52+
* Decodes proto and quality from the ERC1155 takerAssetData of a property-based GodsUnchained order.
53+
*/
54+
export function decodeBrokerAssetData(brokerAssetData: string): GodsUnchainedProperties {
55+
// tslint:disable-next-line:no-unnecessary-type-assertion
56+
const { callbackData: brokerData } = assetDataUtils.decodeAssetDataOrThrow(brokerAssetData) as ERC1155AssetData;
57+
const { propertyData } = brokerDataEncoder.decode(brokerData);
58+
return decodePropertyData(propertyData);
59+
}

contracts/broker/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { artifacts } from './artifacts';
22
export { BrokerContract, GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
3-
export { godsUnchainedUtils } from './gods_unchained_utils';
3+
export * from './gods_unchained_utils';
44
export { BrokerRevertErrors } from '@0x/utils';
55
export {
66
ContractArtifact,

contracts/broker/test/gods_unchained_validator_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contra
22
import { BigNumber } from '@0x/utils';
33
import * as _ from 'lodash';
44

5-
import { godsUnchainedUtils } from '../src/gods_unchained_utils';
5+
import { encodePropertyData } from '../src/gods_unchained_utils';
66

77
import { artifacts } from './artifacts';
88
import { GodsUnchainedValidatorContract, TestGodsUnchainedContract } from './wrappers';
@@ -33,7 +33,7 @@ blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
3333
describe('checkBrokerAsset', () => {
3434
const proto = new BigNumber(42);
3535
const quality = new BigNumber(7);
36-
const propertyData = godsUnchainedUtils.encodePropertyData(proto, quality);
36+
const propertyData = encodePropertyData({ proto, quality });
3737

3838
it('succeeds if assetData proto and quality match propertyData', async () => {
3939
const tokenId = getRandomInteger(0, constants.MAX_UINT256);

contracts/integrations/CHANGELOG.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
{
1010
"note": "Fixed the mainnet dYdX Bridge tests.",
1111
"pr": 2479
12+
},
13+
{
14+
"note": "Addeded decoders for stop-limit data",
15+
"pr": 2484
1216
}
1317
]
1418
},
Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,58 @@
11
import { constants } from '@0x/contracts-test-utils';
22
import { assetDataUtils } from '@0x/order-utils';
3+
import { StaticCallAssetData } from '@0x/types';
34
import { AbiEncoder, BigNumber } from '@0x/utils';
45

6+
export interface StopLimitParameters {
7+
oracle: string;
8+
minPrice: BigNumber;
9+
maxPrice: BigNumber;
10+
}
11+
12+
const stopLimitDataEncoder = AbiEncoder.create([
13+
{ name: 'oracle', type: 'address' },
14+
{ name: 'minPrice', type: 'int256' },
15+
{ name: 'maxPrice', type: 'int256' },
16+
]);
17+
18+
const stopLimitMethodEncoder = AbiEncoder.createMethod('checkStopLimit', [{ name: 'stopLimitData', type: 'bytes' }]);
19+
520
/**
621
* Encodes the given stop limit data parameters into the bytes format expected by the
722
* ChainlinkStopLimit contract.
823
*/
9-
export function encodeChainlinkStopLimitData(oracle: string, minPrice: BigNumber, maxPrice: BigNumber): string {
10-
const encoder = AbiEncoder.create([
11-
{ name: 'oracle', type: 'address' },
12-
{ name: 'minPrice', type: 'int256' },
13-
{ name: 'maxPrice', type: 'int256' },
14-
]);
15-
return encoder.encode({ oracle, minPrice, maxPrice });
24+
export function encodeChainlinkStopLimitData(params: StopLimitParameters): string {
25+
return stopLimitDataEncoder.encode(params);
1626
}
27+
1728
/**
1829
* Encodes the given stop limit data parameters into StaticCall asset data so that it can be used
1930
* in a 0x order.
2031
*/
21-
export function encodeStopLimitStaticCallData(
22-
chainlinkStopLimitAddress: string,
23-
oracle: string,
24-
minPrice: BigNumber,
25-
maxPrice: BigNumber,
26-
): string {
27-
const staticCallData = AbiEncoder.createMethod('checkStopLimit', [{ name: 'stopLimitData', type: 'bytes' }]).encode(
28-
{ stopLimitData: encodeChainlinkStopLimitData(oracle, minPrice, maxPrice) },
29-
);
32+
export function encodeStopLimitStaticCallData(chainlinkStopLimitAddress: string, params: StopLimitParameters): string {
33+
const staticCallData = stopLimitMethodEncoder.encode({
34+
stopLimitData: encodeChainlinkStopLimitData(params),
35+
});
3036
return assetDataUtils.encodeStaticCallAssetData(
3137
chainlinkStopLimitAddress,
3238
staticCallData,
3339
constants.KECCAK256_NULL,
3440
);
3541
}
42+
43+
/**
44+
* Decodes stop limit data parameters from the bytes format expected by the ChainlinkStopLimit contract.
45+
*/
46+
export function decodeChainlinkStopLimitData(stopLimitData: string): StopLimitParameters {
47+
return stopLimitDataEncoder.decode(stopLimitData);
48+
}
49+
50+
/**
51+
* Decodes stop limit data parameters from stop limit StaticCall asset data.
52+
*/
53+
export function decodeStopLimitStaticCallData(assetData: string): StopLimitParameters {
54+
// tslint:disable-next-line:no-unnecessary-type-assertion
55+
const { staticCallData } = assetDataUtils.decodeAssetDataOrThrow(assetData) as StaticCallAssetData;
56+
const stopLimitData = stopLimitMethodEncoder.strictDecode<string>(staticCallData);
57+
return decodeChainlinkStopLimitData(stopLimitData);
58+
}

contracts/integrations/test/broker/broker_test.ts

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import {
22
artifacts as BrokerArtifacts,
33
BrokerContract,
4-
godsUnchainedUtils,
4+
decodeBrokerAssetData,
5+
decodePropertyData,
6+
encodeBrokerAssetData,
7+
encodePropertyData,
58
GodsUnchainedValidatorContract,
69
TestGodsUnchainedContract,
710
} from '@0x/contracts-broker';
811
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
912
import { ExchangeFunctionName, ExchangeRevertErrors } from '@0x/contracts-exchange';
1013
import { ReferenceFunctions } from '@0x/contracts-exchange-libs';
11-
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils';
14+
import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
1215
import { assetDataUtils } from '@0x/order-utils';
1316
import { SignedOrder } from '@0x/types';
1417
import { BigNumber } from '@0x/utils';
@@ -72,13 +75,10 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => {
7275
deployment.tokens.weth.address,
7376
);
7477

75-
const takerAssetData = godsUnchainedUtils.encodeBrokerAssetData(
76-
broker.address,
77-
godsUnchained.address,
78-
validator.address,
79-
makerSpecifiedProto,
80-
makerSpecifiedQuality,
81-
);
78+
const takerAssetData = encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, {
79+
proto: makerSpecifiedProto,
80+
quality: makerSpecifiedQuality,
81+
});
8282

8383
const orderConfig = {
8484
feeRecipientAddress: constants.NULL_ADDRESS,
@@ -530,22 +530,16 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => {
530530

531531
orders = [
532532
await maker.signOrderAsync({
533-
takerAssetData: godsUnchainedUtils.encodeBrokerAssetData(
534-
broker.address,
535-
godsUnchained.address,
536-
validator.address,
537-
firstOrderProto,
538-
firstOrderQuality,
539-
),
533+
takerAssetData: encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, {
534+
proto: firstOrderProto,
535+
quality: firstOrderQuality,
536+
}),
540537
}),
541538
await maker.signOrderAsync({
542-
takerAssetData: godsUnchainedUtils.encodeBrokerAssetData(
543-
broker.address,
544-
godsUnchained.address,
545-
validator.address,
546-
secondOrderProto,
547-
secondOrderQuality,
548-
),
539+
takerAssetData: encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, {
540+
proto: secondOrderProto,
541+
quality: secondOrderQuality,
542+
}),
549543
}),
550544
];
551545
});
@@ -718,4 +712,30 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => {
718712
balanceStore.assertEquals(expectedBalances);
719713
});
720714
});
715+
716+
describe('Data encoding/decoding tools', () => {
717+
const MAX_UINT8 = 2 ** 8 - 1;
718+
const MAX_UINT16 = 2 ** 16 - 1;
719+
720+
it('correctly decodes property data', async () => {
721+
const properties = {
722+
proto: getRandomInteger(0, MAX_UINT16),
723+
quality: getRandomInteger(0, MAX_UINT8),
724+
};
725+
const encoded = encodePropertyData(properties);
726+
const decoded = decodePropertyData(encoded);
727+
expect(decoded.proto).to.bignumber.equal(properties.proto);
728+
expect(decoded.quality).to.bignumber.equal(properties.quality);
729+
});
730+
it('correctly decodes broker asset data', async () => {
731+
const properties = {
732+
proto: getRandomInteger(0, MAX_UINT16),
733+
quality: getRandomInteger(0, MAX_UINT8),
734+
};
735+
const encoded = encodeBrokerAssetData(randomAddress(), randomAddress(), randomAddress(), properties);
736+
const decoded = decodeBrokerAssetData(encoded);
737+
expect(decoded.proto).to.bignumber.equal(properties.proto);
738+
expect(decoded.quality).to.bignumber.equal(properties.quality);
739+
});
740+
});
721741
}); // tslint:disable-line:max-file-line-count

0 commit comments

Comments
 (0)