573b63adc9 | ||
---|---|---|
_assets | ||
client | ||
contracts | ||
migrations | ||
scripts | ||
test | ||
.dockerignore | ||
.gitattributes | ||
.gitignore | ||
CHANGELOG.md | ||
LICENSE | ||
Makefile | ||
README.md | ||
env.example | ||
package.json | ||
truffle-config.js | ||
yarn.lock |
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.