mirror of
https://github.com/status-im/keycard-redeem.git
synced 2025-01-29 21:36:13 +00:00
Merge pull request #3 from status-im/add-block-num
add block number and hash
This commit is contained in:
commit
46ee4862b4
@ -132,6 +132,13 @@ interface SignRedeemResponse {
|
||||
|
||||
async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: string, message: RedeemMessage): Promise<SignRedeemResponse> {
|
||||
const chainId = await config.web3!.eth.net.getId();
|
||||
const block = await config.web3!.eth.getBlock("latest");
|
||||
const finalMessage = {
|
||||
...message,
|
||||
blockNumber: block.number,
|
||||
blockHash: block.hash
|
||||
};
|
||||
|
||||
const domain = [
|
||||
{ name: "name", type: "string" },
|
||||
{ name: "version", type: "string" },
|
||||
@ -140,6 +147,8 @@ async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: s
|
||||
];
|
||||
|
||||
const redeem = [
|
||||
{ name: "blockNumber", type: "uint256" },
|
||||
{ name: "blockHash", type: "bytes32" },
|
||||
{ name: "receiver", type: "address" },
|
||||
{ name: "code", type: "bytes32" },
|
||||
];
|
||||
@ -158,7 +167,7 @@ async function signRedeem(web3Type: Web3Type, contractAddress: string, signer: s
|
||||
},
|
||||
primaryType: ("Redeem" as const),
|
||||
domain: domainData,
|
||||
message: message
|
||||
message: finalMessage
|
||||
};
|
||||
|
||||
if (web3Type === Web3Type.Status) {
|
||||
|
@ -13,6 +13,8 @@ contract GiftBucket {
|
||||
|
||||
uint256 public expirationTime;
|
||||
|
||||
uint256 constant maxTxDelayInBlocks = 10;
|
||||
|
||||
struct Gift {
|
||||
address recipient;
|
||||
uint256 amount;
|
||||
@ -22,6 +24,8 @@ contract GiftBucket {
|
||||
mapping(address => Gift) public gifts;
|
||||
|
||||
struct Redeem {
|
||||
uint256 blockNumber;
|
||||
bytes32 blockHash;
|
||||
address receiver;
|
||||
bytes32 code;
|
||||
}
|
||||
@ -29,7 +33,7 @@ contract GiftBucket {
|
||||
uint256 public redeemableSupply;
|
||||
|
||||
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
|
||||
bytes32 constant REDEEM_TYPEHASH = keccak256("Redeem(address receiver,bytes32 code)");
|
||||
bytes32 constant REDEEM_TYPEHASH = keccak256("Redeem(uint256 blockNumber,bytes32 blockHash,address receiver,bytes32 code)");
|
||||
bytes32 DOMAIN_SEPARATOR;
|
||||
|
||||
modifier onlyOwner() {
|
||||
@ -99,6 +103,10 @@ contract GiftBucket {
|
||||
}
|
||||
|
||||
function redeem(Redeem calldata _redeem, bytes calldata sig) external {
|
||||
require(_redeem.blockNumber < block.number, "transaction cannot be in the future");
|
||||
require(_redeem.blockNumber >= (block.number - maxTxDelayInBlocks), "transaction too old");
|
||||
require(_redeem.blockHash == blockhash(_redeem.blockNumber), "invalid block hash");
|
||||
|
||||
require(block.timestamp < expirationTime, "expired gift");
|
||||
|
||||
address recipient = recoverSigner(_redeem, sig);
|
||||
@ -130,6 +138,8 @@ contract GiftBucket {
|
||||
function hashRedeem(Redeem memory _redeem) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encode(
|
||||
REDEEM_TYPEHASH,
|
||||
_redeem.blockNumber,
|
||||
_redeem.blockHash,
|
||||
_redeem.receiver,
|
||||
_redeem.code
|
||||
));
|
||||
|
@ -54,6 +54,8 @@ async function signRedeem(contractAddress, signer, message) {
|
||||
];
|
||||
|
||||
const redeem = [
|
||||
{ name: "blockNumber", type: "uint256" },
|
||||
{ name: "blockHash", type: "bytes32" },
|
||||
{ name: "receiver", type: "address" },
|
||||
{ name: "code", type: "bytes32" },
|
||||
];
|
||||
@ -256,7 +258,7 @@ contract("GiftBucket", function () {
|
||||
}
|
||||
});
|
||||
|
||||
async function testRedeem(receiver, recipient, signer, relayer, redeemCode) {
|
||||
async function testRedeem(receiver, recipient, signer, relayer, redeemCode, blockNumber, blockHash) {
|
||||
let initialBucketBalance = await TestToken.methods.balanceOf(GiftBucket._address).call();
|
||||
let initialUserBalance = await TestToken.methods.balanceOf(user).call();
|
||||
let initialRedeemableSupply = await GiftBucket.methods.redeemableSupply().call();
|
||||
@ -265,6 +267,8 @@ contract("GiftBucket", function () {
|
||||
const amount = parseInt(gift.amount);
|
||||
|
||||
const message = {
|
||||
blockNumber: blockNumber,
|
||||
blockHash: blockHash,
|
||||
receiver: receiver,
|
||||
code: redeemCode,
|
||||
};
|
||||
@ -293,9 +297,11 @@ contract("GiftBucket", function () {
|
||||
}
|
||||
|
||||
it("cannot redeem after expiration date", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
await mineAt(EXPIRATION_TIME);
|
||||
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE);
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
|
||||
assert.fail("redeem should have failed");
|
||||
} catch(e) {
|
||||
assert.match(e.message, /expired/);
|
||||
@ -303,9 +309,10 @@ contract("GiftBucket", function () {
|
||||
});
|
||||
|
||||
it("cannot redeem with invalid code", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
await mineAt(NOW);
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, web3.utils.sha3("bad-code"));
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, web3.utils.sha3("bad-code"), block.number, block.hash);
|
||||
assert.fail("redeem should have failed");
|
||||
} catch(e) {
|
||||
assert.match(e.message, /invalid code/);
|
||||
@ -313,18 +320,53 @@ contract("GiftBucket", function () {
|
||||
});
|
||||
|
||||
it("cannot redeem with invalid recipient", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
await mineAt(NOW);
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_2, relayer, REDEEM_CODE);
|
||||
await testRedeem(user, keycard_1, keycard_2, relayer, REDEEM_CODE, block.number, block.hash);
|
||||
assert.fail("redeem should have failed");
|
||||
} catch(e) {
|
||||
assert.match(e.message, /not found/);
|
||||
}
|
||||
});
|
||||
|
||||
it("can redeem before expiration date", async function() {
|
||||
it("cannot redeem with a block in the future", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
await mineAt(NOW);
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE);
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, (block.number + 2), "0x0000000000000000000000000000000000000000000000000000000000000000");
|
||||
} catch (e) {
|
||||
assert.match(e.message, /future/);
|
||||
}
|
||||
});
|
||||
|
||||
it("cannot redeem with an old block", async function() {
|
||||
const currentBlock = await web3.eth.getBlock("latest");
|
||||
const block = await web3.eth.getBlock(currentBlock.number - 10);
|
||||
|
||||
await mineAt(NOW);
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
|
||||
} catch (e) {
|
||||
assert.match(e.message, /too old/);
|
||||
}
|
||||
});
|
||||
|
||||
it("cannot redeem with an invalid hash", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
|
||||
await mineAt(NOW);
|
||||
try {
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, "0x0000000000000000000000000000000000000000000000000000000000000000");
|
||||
} catch (e) {
|
||||
assert.match(e.message, /invalid block hash/);
|
||||
}
|
||||
});
|
||||
|
||||
it("can redeem before expiration date", async function() {
|
||||
const block = await web3.eth.getBlock("latest");
|
||||
await mineAt(NOW);
|
||||
await testRedeem(user, keycard_1, keycard_1, relayer, REDEEM_CODE, block.number, block.hash);
|
||||
});
|
||||
|
||||
async function testKill() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user