aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol101
-rw-r--r--packages/contracts/contracts/tokens/YesComplianceToken/YesComplianceToken.sol119
2 files changed, 220 insertions, 0 deletions
diff --git a/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol b/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol
new file mode 100644
index 000000000..646a9f3b8
--- /dev/null
+++ b/packages/contracts/contracts/extensions/CompliantForwarder/CompliantForwarder.sol
@@ -0,0 +1,101 @@
+/*
+
+ 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.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../protocol/Exchange/interfaces/IExchange.sol";
+import "../../tokens/ERC721Token/IERC721Token.sol";
+import "../../utils/LibBytes/LibBytes.sol";
+
+contract CompliantForwarder {
+
+ using LibBytes for bytes;
+
+ bytes4 constant internal EXCHANGE_FILL_ORDER_SELECTOR = bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"));
+ IExchange internal EXCHANGE;
+ IERC721Token internal COMPLIANCE_TOKEN;
+
+ constructor(address exchange, address complianceToken)
+ public
+ {
+ EXCHANGE = IExchange(exchange);
+ COMPLIANCE_TOKEN = IERC721Token(complianceToken);
+ }
+
+ function fillOrder(
+ uint256 salt,
+ address signerAddress,
+ bytes signedFillOrderTransaction,
+ bytes signature
+ )
+ public
+ {
+ // Validate `signedFillOrderTransaction`
+ bytes4 selector = signedFillOrderTransaction.readBytes4(0);
+ if (selector != EXCHANGE_FILL_ORDER_SELECTOR) {
+ revert("EXCHANGE_TRANSACTION_NOT_FILL_ORDER");
+ }
+
+ // Extract maker address from fill order transaction
+ // Below is the table of calldata offsets into a fillOrder transaction.
+ /**
+ ### parameters
+ 0x00 ptr<order>
+ 0x20 takerAssetFillAmount
+ 0x40 ptr<signature>
+ ### order
+ 0x60 makerAddress
+ 0x80 takerAddress
+ 0xa0 feeRecipientAddress
+ 0xc0 senderAddress
+ 0xe0 makerAssetAmount
+ 0x100 takerAssetAmount
+ 0x120 makerFee
+ 0x140 takerFee
+ 0x160 expirationTimeSeconds
+ 0x180 salt
+ 0x1a0 ptr<makerAssetData>
+ 0x1c0 ptr<takerAssetData>
+ 0x1e0 makerAssetData
+ * takerAssetData
+ * signature
+ ------------------------------
+ * Context-dependent offsets, unknown at compile time.
+ */
+ // Add 0x4 to a given offset to account for the fillOrder selector prepended to `signedFillOrderTransaction`.
+ // Add 0xc to the makerAddress since abi-encoded addresses are left padded with 12 bytes.
+ // Putting this together: makerAddress = 0x60 + 0x4 + 0xc = 0x70
+ address makerAddress = signedFillOrderTransaction.readAddress(0x70);
+
+ // Verify maker/taker have been verified by the compliance token.
+ if (COMPLIANCE_TOKEN.balanceOf(makerAddress) == 0) {
+ revert("MAKER_UNVERIFIED");
+ } else if (COMPLIANCE_TOKEN.balanceOf(signerAddress) == 0) {
+ revert("TAKER_UNVERIFIED");
+ }
+
+ // All entities are verified. Execute fillOrder.
+ EXCHANGE.executeTransaction(
+ salt,
+ signerAddress,
+ signedFillOrderTransaction,
+ signature
+ );
+ }
+} \ No newline at end of file
diff --git a/packages/contracts/contracts/tokens/YesComplianceToken/YesComplianceToken.sol b/packages/contracts/contracts/tokens/YesComplianceToken/YesComplianceToken.sol
new file mode 100644
index 000000000..abd04219d
--- /dev/null
+++ b/packages/contracts/contracts/tokens/YesComplianceToken/YesComplianceToken.sol
@@ -0,0 +1,119 @@
+pragma solidity ^0.4.24;
+
+import "../ERC721Token/ERC721Token.sol";
+
+/**
+ * @notice an ERC721 "yes" compliance token supporting a collection of country-specific attributions which answer specific
+ * compliance-related queries with YES. (attestations)
+ *
+ * primarily ERC721 is useful for the self-management of claiming addresses. a single token is more useful
+ * than a non-ERC721 interface because of interop with other 721-supporting systems/ui; it allows users to
+ * manage their financial stamp with flexibility using a well-established simple concept of non-fungible tokens.
+ * this interface is for anyone needing to carry around and otherwise manage their proof of compliance.
+ *
+ * the financial systems these users authenticate against have a different set of API requirements. they need
+ * more contextualization ability than a balance check to support distinctions of attestations, as well as geographic
+ * distinction. these integrations are made simpler as the language of the query more closely match the language of compliance.
+ *
+ * this interface describes, beyond 721, these simple compliance-specific interfaces (and their management tools)
+ *
+ * notes:
+ * - no address can be associated with more than one identity (though addresses may have more than token). issuance
+ * in this circumstance will fail
+ * - one person or business = one entity
+ * - one entity may have many tokens across many addresses; they can mint and burn tokens tied to their identity at will
+ * - two token types: control & non-control. both carry compliance proof
+ * - control tokens let their holders mint and burn (within the same entity)
+ * - non-control tokens are solely for compliance queries
+ * - a lock on the entity is used instead of token revocation to remove the cash burden assumed by a customer to
+ * redistribute a fleet of coins
+ * - all country codes should be via ISO-3166-1
+ *
+ * any (non-view) methods not explicitly marked idempotent are not idempotent.
+ */
+contract YesComplianceTokenV1 is ERC721Token /*, ERC165 :should: */ {
+
+ uint256 public constant OWNER_ENTITY_ID = 1;
+
+ uint8 public constant YESMARK_OWNER = 128;
+ uint8 public constant YESMARK_VALIDATOR = 129;
+
+ /*
+ todo events: entity updated, destroyed, ????
+ Finalized
+ Attested
+
+ */
+
+ /**
+ * @notice query api: returns true if the specified address has the given country/yes attestation. this
+ * is the primary method partners will use to query the active qualifications of any particular
+ * address.
+ */
+ function isYes(uint256 _validatorEntityId, address _address, uint16 _countryCode, uint8 _yes) external view returns(bool) ;
+
+ /** @notice same as isYes except as an imperative */
+ function requireYes(uint256 _validatorEntityId, address _address, uint16 _countryCode, uint8 _yes) external view ;
+
+ /**
+ * @notice retrieve all YES marks for an address in a particular country
+ * @param _validatorEntityId the validator ID to consider. or, use 0 for any of them
+ * @param _address the validator ID to consider, or 0 for any of them
+ * @param _countryCode the ISO-3166-1 country code
+ * @return (non-duplicate) array of YES marks present
+ */
+ function getYes(uint256 _validatorEntityId, address _address, uint16 _countryCode) external view returns(uint8[] /* memory */);
+
+ // function getCountries(uint256 _validatorEntityId, address _address) external view returns(uint16[] /* memory */);
+
+ /**
+ * @notice create new tokens. fail if _to already
+ * belongs to a different entity and caller is not validator
+ * @param _control true if the new token is a control token (can mint, burn). aka NOT limited.
+ * @param _entityId the entity to mint for, supply 0 to use the entity tied to the caller
+ * @return the newly created token ID
+ */
+ function mint(address _to, uint256 _entityId, bool _control) external returns (uint256);
+
+ /** @notice shortcut to mint() + setYes() in one call, for a single country */
+ function mint(address _to, uint256 _entityId, bool _control, uint16 _countryCode, uint8[] _yes) external returns (uint256);
+
+ /** @notice destroys a specific token */
+ function burn(uint256 _tokenId) external;
+
+ /** @notice destroys the entire entity and all tokens */
+ function burnEntity(uint256 _entityId) external;
+
+ /**
+ * @notice adds a specific attestations (yes) to an entity. idempotent: will return normally even if the mark
+ * was already set by this validator
+ */
+ function setYes(uint256 _entityId, uint16 _countryCode, uint8 _yes) external;
+
+ /**
+ * @notice removes a attestation(s) from a specific validator for an entity. idempotent
+ */
+ function clearYes(uint256 _entityId, uint16 _countryCode, uint8 _yes) external;
+
+ /** @notice removes all attestations in a given country for a particular entity. idempotent */
+ function clearYes(uint256 _entityId, uint16 _countryCode) external;
+
+ /** @notice removes all attestations for a particular entity. idempotent */
+ function clearYes(uint256 _entityId) external;
+
+ /** @notice assigns a lock to an entity, rendering all isYes queries false. idempotent */
+ function setLocked(uint256 _entityId, bool _lock) external;
+
+ /** @notice checks whether or not a particular entity is locked */
+ function isLocked(uint256 _entityId) external view returns(bool);
+
+ /** @notice returns true if the specified token has been finalized (cannot be moved) */
+ function isFinalized(uint256 _tokenId) external view returns(bool);
+
+ /** @notice finalizes a token by ID preventing it from getting moved. idempotent */
+ function finalize(uint256 _tokenId) external;
+
+ /** @return the entity ID associated with an address (or fail if there is not one) */
+ function getEntityId(address _address) external view returns(uint256);
+
+} \ No newline at end of file