This repository has been archived by the owner on Jul 9, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 465
Add decoders for broker and stop-limit data #2484
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,59 @@ | ||
import { assetDataUtils } from '@0x/order-utils'; | ||
import { ERC1155AssetData } from '@0x/types'; | ||
import { AbiEncoder, BigNumber } from '@0x/utils'; | ||
|
||
export const godsUnchainedUtils = { | ||
/** | ||
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator. | ||
*/ | ||
encodePropertyData(proto: BigNumber, quality: BigNumber): string { | ||
return AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]).encode({ | ||
proto, | ||
quality, | ||
}); | ||
}, | ||
/** | ||
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData | ||
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker, | ||
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies | ||
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the | ||
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards | ||
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails. | ||
*/ | ||
encodeBrokerAssetData( | ||
brokerAddress: string, | ||
godsUnchainedAddress: string, | ||
validatorAddress: string, | ||
proto: BigNumber, | ||
quality: BigNumber, | ||
bundleSize: number = 1, | ||
): string { | ||
const dataEncoder = AbiEncoder.create([ | ||
{ name: 'godsUnchainedAddress', type: 'address' }, | ||
{ name: 'validatorAddress', type: 'address' }, | ||
{ name: 'propertyData', type: 'bytes' }, | ||
]); | ||
const propertyData = AbiEncoder.create([ | ||
{ name: 'proto', type: 'uint16' }, | ||
{ name: 'quality', type: 'uint8' }, | ||
]).encode({ proto, quality }); | ||
const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData }); | ||
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data); | ||
}, | ||
}; | ||
export interface GodsUnchainedProperties { | ||
proto: BigNumber; | ||
quality: BigNumber; | ||
} | ||
|
||
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]); | ||
const brokerDataEncoder = AbiEncoder.create([ | ||
{ name: 'godsUnchainedAddress', type: 'address' }, | ||
{ name: 'validatorAddress', type: 'address' }, | ||
{ name: 'propertyData', type: 'bytes' }, | ||
]); | ||
|
||
/** | ||
* Encodes the given proto and quality into the bytes format expected by the GodsUnchainedValidator. | ||
*/ | ||
export function encodePropertyData(properties: GodsUnchainedProperties): string { | ||
return propertyDataEncoder.encode(properties); | ||
} | ||
|
||
/** | ||
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData | ||
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker, | ||
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies | ||
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the | ||
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards | ||
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails. | ||
*/ | ||
export function encodeBrokerAssetData( | ||
brokerAddress: string, | ||
godsUnchainedAddress: string, | ||
validatorAddress: string, | ||
properties: GodsUnchainedProperties, | ||
bundleSize: number = 1, | ||
): string { | ||
const propertyData = propertyDataEncoder.encode(properties); | ||
const brokerData = brokerDataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData }); | ||
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], brokerData); | ||
} | ||
|
||
/** | ||
* Decodes proto and quality from the bytes format expected by the GodsUnchainedValidator. | ||
*/ | ||
export function decodePropertyData(propertyData: string): GodsUnchainedProperties { | ||
return propertyDataEncoder.decode(propertyData); | ||
} | ||
|
||
/** | ||
* Decodes proto and quality from the ERC1155 takerAssetData of a property-based GodsUnchained order. | ||
*/ | ||
export function decodeBrokerAssetData(brokerAssetData: string): GodsUnchainedProperties { | ||
// tslint:disable-next-line:no-unnecessary-type-assertion | ||
const { callbackData: brokerData } = assetDataUtils.decodeAssetDataOrThrow(brokerAssetData) as ERC1155AssetData; | ||
const { propertyData } = brokerDataEncoder.decode(brokerData); | ||
return decodePropertyData(propertyData); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,58 @@ | ||
import { constants } from '@0x/contracts-test-utils'; | ||
import { assetDataUtils } from '@0x/order-utils'; | ||
import { StaticCallAssetData } from '@0x/types'; | ||
import { AbiEncoder, BigNumber } from '@0x/utils'; | ||
|
||
export interface StopLimitParameters { | ||
oracle: string; | ||
minPrice: BigNumber; | ||
maxPrice: BigNumber; | ||
} | ||
|
||
const stopLimitDataEncoder = AbiEncoder.create([ | ||
{ name: 'oracle', type: 'address' }, | ||
{ name: 'minPrice', type: 'int256' }, | ||
{ name: 'maxPrice', type: 'int256' }, | ||
]); | ||
|
||
const stopLimitMethodEncoder = AbiEncoder.createMethod('checkStopLimit', [{ name: 'stopLimitData', type: 'bytes' }]); | ||
|
||
/** | ||
* Encodes the given stop limit data parameters into the bytes format expected by the | ||
* ChainlinkStopLimit contract. | ||
*/ | ||
export function encodeChainlinkStopLimitData(oracle: string, minPrice: BigNumber, maxPrice: BigNumber): string { | ||
const encoder = AbiEncoder.create([ | ||
{ name: 'oracle', type: 'address' }, | ||
{ name: 'minPrice', type: 'int256' }, | ||
{ name: 'maxPrice', type: 'int256' }, | ||
]); | ||
return encoder.encode({ oracle, minPrice, maxPrice }); | ||
export function encodeChainlinkStopLimitData(params: StopLimitParameters): string { | ||
return stopLimitDataEncoder.encode(params); | ||
} | ||
|
||
/** | ||
* Encodes the given stop limit data parameters into StaticCall asset data so that it can be used | ||
* in a 0x order. | ||
*/ | ||
export function encodeStopLimitStaticCallData( | ||
chainlinkStopLimitAddress: string, | ||
oracle: string, | ||
minPrice: BigNumber, | ||
maxPrice: BigNumber, | ||
): string { | ||
const staticCallData = AbiEncoder.createMethod('checkStopLimit', [{ name: 'stopLimitData', type: 'bytes' }]).encode( | ||
{ stopLimitData: encodeChainlinkStopLimitData(oracle, minPrice, maxPrice) }, | ||
); | ||
export function encodeStopLimitStaticCallData(chainlinkStopLimitAddress: string, params: StopLimitParameters): string { | ||
const staticCallData = stopLimitMethodEncoder.encode({ | ||
stopLimitData: encodeChainlinkStopLimitData(params), | ||
}); | ||
return assetDataUtils.encodeStaticCallAssetData( | ||
chainlinkStopLimitAddress, | ||
staticCallData, | ||
constants.KECCAK256_NULL, | ||
); | ||
} | ||
|
||
/** | ||
* Decodes stop limit data parameters from the bytes format expected by the ChainlinkStopLimit contract. | ||
*/ | ||
export function decodeChainlinkStopLimitData(stopLimitData: string): StopLimitParameters { | ||
return stopLimitDataEncoder.decode(stopLimitData); | ||
} | ||
|
||
/** | ||
* Decodes stop limit data parameters from stop limit StaticCall asset data. | ||
*/ | ||
export function decodeStopLimitStaticCallData(assetData: string): StopLimitParameters { | ||
// tslint:disable-next-line:no-unnecessary-type-assertion | ||
const { staticCallData } = assetDataUtils.decodeAssetDataOrThrow(assetData) as StaticCallAssetData; | ||
const stopLimitData = stopLimitMethodEncoder.strictDecode<string>(staticCallData); | ||
return decodeChainlinkStopLimitData(stopLimitData); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
import { | ||
artifacts as BrokerArtifacts, | ||
BrokerContract, | ||
godsUnchainedUtils, | ||
decodeBrokerAssetData, | ||
decodePropertyData, | ||
encodeBrokerAssetData, | ||
encodePropertyData, | ||
GodsUnchainedValidatorContract, | ||
TestGodsUnchainedContract, | ||
} from '@0x/contracts-broker'; | ||
import { DummyERC721TokenContract } from '@0x/contracts-erc721'; | ||
import { ExchangeFunctionName, ExchangeRevertErrors } from '@0x/contracts-exchange'; | ||
import { ReferenceFunctions } from '@0x/contracts-exchange-libs'; | ||
import { blockchainTests, constants, expect } from '@0x/contracts-test-utils'; | ||
import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils'; | ||
import { assetDataUtils } from '@0x/order-utils'; | ||
import { SignedOrder } from '@0x/types'; | ||
import { BigNumber } from '@0x/utils'; | ||
|
@@ -72,13 +75,10 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => { | |
deployment.tokens.weth.address, | ||
); | ||
|
||
const takerAssetData = godsUnchainedUtils.encodeBrokerAssetData( | ||
broker.address, | ||
godsUnchained.address, | ||
validator.address, | ||
makerSpecifiedProto, | ||
makerSpecifiedQuality, | ||
); | ||
const takerAssetData = encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, { | ||
proto: makerSpecifiedProto, | ||
quality: makerSpecifiedQuality, | ||
}); | ||
|
||
const orderConfig = { | ||
feeRecipientAddress: constants.NULL_ADDRESS, | ||
|
@@ -530,22 +530,16 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => { | |
|
||
orders = [ | ||
await maker.signOrderAsync({ | ||
takerAssetData: godsUnchainedUtils.encodeBrokerAssetData( | ||
broker.address, | ||
godsUnchained.address, | ||
validator.address, | ||
firstOrderProto, | ||
firstOrderQuality, | ||
), | ||
takerAssetData: encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, { | ||
proto: firstOrderProto, | ||
quality: firstOrderQuality, | ||
}), | ||
}), | ||
await maker.signOrderAsync({ | ||
takerAssetData: godsUnchainedUtils.encodeBrokerAssetData( | ||
broker.address, | ||
godsUnchained.address, | ||
validator.address, | ||
secondOrderProto, | ||
secondOrderQuality, | ||
), | ||
takerAssetData: encodeBrokerAssetData(broker.address, godsUnchained.address, validator.address, { | ||
proto: secondOrderProto, | ||
quality: secondOrderQuality, | ||
}), | ||
}), | ||
]; | ||
}); | ||
|
@@ -718,4 +712,30 @@ blockchainTests.resets('Broker <> Gods Unchained integration tests', env => { | |
balanceStore.assertEquals(expectedBalances); | ||
}); | ||
}); | ||
|
||
describe('Data encoding/decoding tools', () => { | ||
const MAX_UINT8 = new BigNumber(2).exponentiatedBy(8).minus(1); | ||
const MAX_UINT16 = new BigNumber(2).exponentiatedBy(16).minus(1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Seems clearer to just do like |
||
|
||
it('correctly decodes property data', async () => { | ||
const properties = { | ||
proto: getRandomInteger(0, MAX_UINT16), | ||
quality: getRandomInteger(0, MAX_UINT8), | ||
}; | ||
const encoded = encodePropertyData(properties); | ||
const decoded = decodePropertyData(encoded); | ||
expect(decoded.proto).to.bignumber.equal(properties.proto); | ||
expect(decoded.quality).to.bignumber.equal(properties.quality); | ||
}); | ||
it('correctly decodes broker asset data', async () => { | ||
const properties = { | ||
proto: getRandomInteger(0, MAX_UINT16), | ||
quality: getRandomInteger(0, MAX_UINT8), | ||
}; | ||
const encoded = encodeBrokerAssetData(randomAddress(), randomAddress(), randomAddress(), properties); | ||
const decoded = decodeBrokerAssetData(encoded); | ||
expect(decoded.proto).to.bignumber.equal(properties.proto); | ||
expect(decoded.quality).to.bignumber.equal(properties.quality); | ||
}); | ||
}); | ||
}); // tslint:disable-line:max-file-line-count |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would the abi encoder accept these if they were just
number
s?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah yeah, changed to
BigNumber | number