Go to file
dependabot[bot] 3f5b85c289
Bump qs from 6.5.2 to 6.5.3 in /scripts
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

---
updated-dependencies:
- dependency-name: qs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-11 19:56:52 +00:00
_assets fix redeem dockerfile to compile contracts 2020-07-01 13:16:11 +02:00
client add homepage with authentication 2020-09-29 10:30:17 +02:00
contracts add domain separator to redeem code 2020-10-05 14:25:17 +02:00
migrations add domain separator to redeem code 2020-10-05 14:25:17 +02:00
scripts Bump qs from 6.5.2 to 6.5.3 in /scripts 2022-12-11 19:56:52 +00:00
test add domain separator to redeem code 2020-10-05 14:25:17 +02:00
.dockerignore fix redeem dockerfile to compile contracts 2020-07-01 13:16:11 +02:00
.gitattributes add .gitattributes 2020-02-21 14:00:57 +01:00
.gitignore use truffle contracts in dapp and scripts 2020-09-23 12:05:04 +02:00
CHANGELOG.md initial import 2020-02-18 20:23:31 +01:00
LICENSE initial import 2020-02-18 20:23:31 +01:00
Makefile remove unused vars from Makefile 2020-09-23 12:06:06 +02:00
README.md added README 2020-05-26 11:52:56 +03:00
env.example add truffle migrations and config 2020-09-23 12:05:04 +02:00
package.json use OwnableUpgradeSafe for buckets 2020-10-01 10:01:29 +02:00
truffle-config.js add domain separator to redeem code 2020-10-05 14:25:17 +02:00
yarn.lock use OwnableUpgradeSafe for buckets 2020-10-01 10:01:29 +02:00

README.md

Keycard Redeem

This repository contains SmartContracts, dApps and tools to enable using the Keycard Cash applet as a asset redeeming tool. Usage scenario include gift cards, prepaid cards and general distribution of assets. Both ERC20 and NFT are supported. Beside redeeming the asset, in the NFT scenario, the way mappings are created between card and asset can also be useful for user authentication scenario in a context with lower security concerns, such as ticketing for events.

Redeeming an asset requires, beside the card, also a redeem code which is associated to the redeemable during provisioning. This means that simply tapping the card to a terminal does not allow anybody to redeem the card's asset without knowing this code. This makes it safe to use the card in the aforementioned authentication scenarios, since the presence of a redeemable can be checked without exposing the user to the risk of the asset being stolen. The redeem code can be any 32-byte value, making it possible to use binary data to be printed on QR codes or also alphanumeric codes which can be input by the user. Since the contract data is publicly readable, the keccak256 hash of the code is stored.

The asset are redeemed using the Keycard Cash applet, but the actual the destination of the redeemed asset can be chosen during the process. This can be a regular wallet, including a Keycard Wallet or any other Ethereum Account.

Because of the promotional nature of many of the usage scenarios, we have foreseen the possibility to redeem without the user having to spend gas. For this reason a simple relay is available in the scripts folder. The redeeming dApp supports this relay too.

Smartcontract API

ERC20BucketFactory / NFTBucketFactory

constructor()

Instantiates the factory.

create(address _tokenAddress, uint256 _startTime, uint256 _expirationTime, uint256 _maxTxDelayInBlocks)

Creates an ERC20Bucket or NFTBucket (depending on the factory). The bucket will contain assets of the given _tokenAddress type. The _startTime parameter is the timestamp of the date after which redeem requests will be accepted by the bucket. _expirationTime is the timestamp of the date after which the bucket stops accepting redeem requests, making it possible for the owner to kill() it and reclaim the remaining assets. _maxTxDelayInBlocks is the max number of blocks that can elapse between the redeem request (EIP-712 formatted metatx) being signed by the Cash applet and it being actually submitted to the network. More details on why this is needed as in the bucket documentation below.

ERC20Bucket / NFTBucket (common)

constructor(address _tokenAddress, uint256 _startTime, uint256 _expirationTime, uint256 _maxTxDelayInBlocks)

Creates a bucket. This is usually done through the factory.

redeem(Redeem calldata _redeem, bytes calldata _sig)

This function redeems the asset from the bucket. The Ethereum transaction itself can be signed by any party, but the _signature field must be the signature of the _redeem structure generated by the associated Keycard Cash instance. The transaction will fail if the signature does not match, the _startTime has not yet been reached or _expirationTime has already passed. Additionally the blockNumber cannot be older than _maxTxDelayInBlocks blocks before this transaction is processed. The blockHash is verified to correspond to the blockNumber. This prevents caching signed meta-transactions to be sent later thus reducing the attack routes.

The signature is calculated according to ERC-712. The Redeem structure is as follow

  struct Payment {
    uint256 blockNumber; // The latest block number at the moment of signing this tx
    bytes32 blockHash; // The hash of the block referred by the blockNumber field
    address receiver; // The receiver of the redeemed assets
    bytes32 code; // The redeem code
  }

The domain separator is as follow

    DOMAIN_SEPARATOR = keccak256(abi.encode(
      EIP712DOMAIN_TYPEHASH, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
      keccak256("KeycardNFTBucket"), // KeycardERC20Bucket for ERC20Bucket
      keccak256("1"),
      chainId,
      register // the register associated to this wallet. This makes sure that a transaction can only be submitted to a single network.
    ));

bucketType() returns (uint256)

Returns the type of the bucket, indicated by the ERC number of the asset contained. ERC20 buckets return 20, while NFT buckets returns 721.

kill()

Owner only. Kills the contract. This only succeeds if _expirationTime has already passed. In case of ERC20Bucket the unclaimed assets are sent, in a single transaction, to the owner of the bucket. In case of NFTBucket the owner of the bucket is an approved agent for all assets still owned by the bucket. These NFTs can then be transferred when needed by the owner or left there if their utility is over.

ERC20Bucket specific

createRedeemable(address recipient, uint256 amount, bytes32 code)

Owner only. Creates a redeemable with the given recipient, amount and redeem code. This only succeeds if the recipient does not already have an associated redeemable, and if the bucket owns enough unallocated (i.e. not assigned to redeemables) coins. Coins must have been previously transferred to the contract with a regular ERC20 transaction.

totalSupply() returns (uint256)

Returns the total amount of coins owned by the contract.

availableSupply() returns (uint256)

Returns the amount of coins not yet allocated to redeemables.

NFTBucket specific

onERC721Received(address _operator, address _from, uint256 _tokenID, bytes calldata _data)

Creates a redeemable. This function is a callback which will be called by the NFT token contract itself. You never need to call this directly. To make this happen, you need to make a regular ERC721 transfer with the _data field containing the concatenation of recipient address (20 bytes) and the redeem code (32 bytes). This allows transfering the token and creating the redeemable in a single transaction. The create-redeemable.js script (check the scripts directory) can be used for this.

Scripts

In the scripts directory you find the create-redeemable.js and relay.js scripts. Check the directory's documentation for more details on how to use them.