Skip to content

Commit b2b3d08

Browse files
Merge pull request #151 from gulshanvasnani/executeRedeem
TH ExecuteRedemption
2 parents dcc218d + 1c5ffaf commit b2b3d08

5 files changed

Lines changed: 252 additions & 1 deletion

File tree

lib/ContractInteract/TokenHolder.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,66 @@ class TokenHolder {
139139
return executeRuleCallPrefix;
140140
}
141141

142+
/**
143+
* It is used to get call prefix of executeRedeem method in TokenHolder contract.
144+
*
145+
* @returns {string} Encoded signature of executeRedeem method.
146+
*/
147+
getTokenHolderExecuteRedemptionCallPrefix() {
148+
const executeRedeemHash = this.auxiliaryWeb3.utils.soliditySha3(
149+
'executeRedemption(address,bytes,uint256,bytes32,bytes32,uint8)'
150+
);
151+
const executeRedeemCallPrefix = executeRedeemHash.substring(0, 10);
152+
return executeRedeemCallPrefix;
153+
}
154+
155+
/**
156+
* Executable data for co-gateway redeem.
157+
*
158+
* @param amount Redeem amount that will be transferred from redeemer account.
159+
* @param beneficiary The address in the origin chain where the value tokens will be released.
160+
* @param gasPrice Gas price that redeemer is ready to pay to get the redeem process done.
161+
* @param gasLimit Gas limit that redeemer is ready to pay.
162+
* @param nonce Nonce of the redeemer address.
163+
* @param hashLock Hash Lock provided by the facilitator.
164+
* @returns {*}
165+
*/
166+
getCoGatewayRedeemExecutableData(amount, beneficiary, gasPrice, gasLimit, nonce, hashLock) {
167+
return this.auxiliaryWeb3.eth.abi.encodeFunctionCall(
168+
{
169+
name: 'redeem',
170+
type: 'function',
171+
inputs: [
172+
{
173+
type: 'uint256',
174+
name: 'amount'
175+
},
176+
{
177+
type: 'address',
178+
name: 'beneficiary'
179+
},
180+
{
181+
type: 'uint256',
182+
name: 'gasPrice'
183+
},
184+
{
185+
type: 'uint256',
186+
name: 'gasLimit'
187+
},
188+
{
189+
type: 'uint256',
190+
name: 'nonce'
191+
},
192+
{
193+
type: 'bytes32',
194+
name: 'hashlock'
195+
}
196+
]
197+
},
198+
[amount, beneficiary, gasPrice, gasLimit, nonce, hashLock]
199+
);
200+
}
201+
142202
/**
143203
* It is used to execute executable data signed by a session key.
144204
*
@@ -193,6 +253,60 @@ class TokenHolder {
193253
return Promise.resolve(this.contract.methods.executeRule(to, data, nonce, r, s, v));
194254
}
195255

256+
/**
257+
* It is used to execute redeem data signed by a session key.
258+
*
259+
* @param {string} to The target contract address the transaction will be executed upon.
260+
* @param {string} data The payload of a function to be executed in the target contract.
261+
* @param {string} nonce The nonce of an session key that was used to sign the transaction.
262+
* @param {string} r `r` part of the signature.
263+
* @param {string} s `s` part of the signature.
264+
* @param {string} v `v` part of the signature.
265+
* @param {Object} txOptions Tx options.
266+
*
267+
* @returns {Promise<Object>} Promise that resolves to transaction receipt.
268+
*/
269+
async executeRedemption(to, data, nonce, r, s, v, txOptions) {
270+
if (!txOptions) {
271+
const err = new TypeError('Invalid transaction options.');
272+
return Promise.reject(err);
273+
}
274+
if (!Web3.utils.isAddress(txOptions.from)) {
275+
const err = new TypeError(`Invalid from address: ${txOptions.from}.`);
276+
return Promise.reject(err);
277+
}
278+
const txObject = await this.executeRedemptionRawTx(to, data, nonce, r, s, v);
279+
return Utils.sendTransaction(txObject, txOptions);
280+
}
281+
282+
/**
283+
* Private method which is used to execute redeem data signed by a session key.
284+
*
285+
* @param {string} to The target contract address the transaction will be executed upon.
286+
* @param {string} data The payload of a function to be executed in the target contract.
287+
* @param {string} nonce The nonce of an session key that was used to sign the transaction.
288+
* @param {string} r `r` part of the signature signed by sessionKey.
289+
* @param {string} s `s` part of the signature signed by sessionKey.
290+
* @param {string} v `v` part of the signature signed by sessionKey.
291+
*
292+
* @returns {Promise<Object>} Promise that resolves to raw transaction object.
293+
*/
294+
executeRedemptionRawTx(to, data, nonce, r, s, v) {
295+
if (!Web3.utils.isAddress(to)) {
296+
const err = new TypeError(`Invalid to address: ${to}.`);
297+
return Promise.reject(err);
298+
}
299+
if (!data) {
300+
const err = new TypeError(`Invalid data: ${data}.`);
301+
return Promise.reject(err);
302+
}
303+
if (!nonce) {
304+
const err = new TypeError(`Invalid nonce: ${nonce}.`);
305+
return Promise.reject(err);
306+
}
307+
return Promise.resolve(this.contract.methods.executeRedemption(to, data, nonce, r, s, v));
308+
}
309+
196310
/**
197311
* It returns the session key data object.
198312
*
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
3+
const Web3 = require('web3');
4+
const sinon = require('sinon');
5+
const { assert } = require('chai');
6+
7+
const Spy = require('../../../utils/Spy');
8+
const AssertAsync = require('../../../utils/AssertAsync');
9+
const TokenHolder = require('../../../lib/ContractInteract/TokenHolder');
10+
const Utils = require('../../../utils/Utils');
11+
12+
describe('TokenHolder.executeRedemption()', () => {
13+
let tokenHolder;
14+
15+
beforeEach(() => {
16+
const web3 = new Web3();
17+
const address = '0x0000000000000000000000000000000000000002';
18+
tokenHolder = new TokenHolder(web3, address);
19+
});
20+
21+
it('should construct with correct parameters', async () => {
22+
const to = '0x0000000000000000000000000000000000000003';
23+
const data = null;
24+
const nonce = 1;
25+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
26+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
27+
const v = 28;
28+
const txOptions = {
29+
from: '0x0000000000000000000000000000000000000006'
30+
};
31+
32+
const mockRawTx = 'mockExecuteRedemptionRawTx';
33+
const executeRedemptionRawTx = sinon.replace(tokenHolder, 'executeRedemptionRawTx', sinon.fake.resolves(mockRawTx));
34+
const spySendTransaction = sinon.replace(Utils, 'sendTransaction', sinon.fake.resolves(true));
35+
const response = await tokenHolder.executeRedemption(to, data, nonce, r, s, v, txOptions);
36+
37+
assert.isTrue(response, 'executeRedemption should return true');
38+
Spy.assert(executeRedemptionRawTx, 1, [[to, data, nonce, r, s, v]]);
39+
Spy.assert(spySendTransaction, 1, [[mockRawTx, txOptions]]);
40+
});
41+
42+
it('should throw an error when txOptions is undefined', async () => {
43+
const to = '0x0000000000000000000000000000000000000003';
44+
const data = null;
45+
const nonce = 1;
46+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
47+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
48+
const v = 28;
49+
await AssertAsync.reject(tokenHolder.executeRedemption(to, data, nonce, r, s, v), `Invalid transaction options.`);
50+
});
51+
52+
it('should throw an error when txOptions.from is undefined', async () => {
53+
const to = '0x0000000000000000000000000000000000000003';
54+
const data = null;
55+
const nonce = 1;
56+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
57+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
58+
const v = 28;
59+
await AssertAsync.reject(
60+
tokenHolder.executeRedemption(to, data, nonce, r, s, v, {}),
61+
`Invalid from address: undefined.`
62+
);
63+
});
64+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use strict';
2+
3+
const Web3 = require('web3');
4+
const sinon = require('sinon');
5+
const { assert } = require('chai');
6+
7+
const Spy = require('../../../utils/Spy');
8+
const AssertAsync = require('../../../utils/AssertAsync');
9+
const TokenHolder = require('../../../lib/ContractInteract/TokenHolder');
10+
11+
describe('TokenHolder.executeRedemptionRawTx()', () => {
12+
let tokenHolder;
13+
14+
beforeEach(() => {
15+
const web3 = new Web3();
16+
const tokenHolderAddress = '0x0000000000000000000000000000000000000002';
17+
tokenHolder = new TokenHolder(web3, tokenHolderAddress);
18+
});
19+
20+
it('should construct with correct parameters', async () => {
21+
const to = '0x0000000000000000000000000000000000000003';
22+
const data = 'cogatewayRedeem';
23+
const nonce = 1;
24+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
25+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
26+
const v = 28;
27+
const mockExecuteRedeem = 'mockExecuteRedeem';
28+
const executeRedeemSpy = sinon.replace(
29+
tokenHolder.contract.methods,
30+
'executeRedemption',
31+
sinon.fake.resolves(mockExecuteRedeem)
32+
);
33+
const response = await tokenHolder.executeRedemptionRawTx(to, data, nonce, r, s, v);
34+
35+
assert.strictEqual(response, mockExecuteRedeem);
36+
Spy.assert(executeRedeemSpy, 1, [[to, data, nonce, r, s, v]]);
37+
sinon.restore();
38+
});
39+
40+
it('should throw an error when to address is undefined', async () => {
41+
const to = undefined;
42+
const data = 'cogatewayRedeem';
43+
const nonce = 1;
44+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
45+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
46+
const v = 28;
47+
await AssertAsync.reject(
48+
tokenHolder.executeRedemptionRawTx(to, data, nonce, r, s, v),
49+
`Invalid to address: undefined.`
50+
);
51+
});
52+
53+
it('should throw an error when data is undefined', async () => {
54+
const to = '0x0000000000000000000000000000000000000003';
55+
const data = undefined;
56+
const nonce = 1;
57+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
58+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
59+
const v = 28;
60+
await AssertAsync.reject(tokenHolder.executeRedemptionRawTx(to, data, nonce, r, s, v), `Invalid data: undefined.`);
61+
});
62+
63+
it('should throw an error when nonce is undefined', async () => {
64+
const to = '0x0000000000000000000000000000000000000003';
65+
const data = 'cogatewayRedeem';
66+
const nonce = undefined;
67+
const r = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1760';
68+
const s = '0xdfc2f04f19e6d253cd3980e663f57600f33fc1a16697f5e9703e8dc23d2d1c1';
69+
const v = 28;
70+
await AssertAsync.reject(tokenHolder.executeRedemptionRawTx(to, data, nonce, r, s, v), `Invalid nonce: undefined.`);
71+
});
72+
});

test/unit/TokenHolder/executeRuleRawTx.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe('TokenHolder.executeRuleRawTx()', () => {
3535

3636
assert.strictEqual(response, mockExecuteRule);
3737
Spy.assert(executeRuleSpy, 1, [[to, data, nonce, r, s, v]]);
38+
sinon.restore();
3839
});
3940

4041
it('should throw an error when to address is undefined', async () => {

utils/AssertAsync.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const { assert } = require('chai');
44

55
/**
6-
* This class includes the utitity assert function
6+
* This class includes the utility assert function
77
*/
88
class AssertAsync {
99
static async reject(promise, message) {

0 commit comments

Comments
 (0)